diff options
Diffstat (limited to 'src')
172 files changed, 8206 insertions, 5569 deletions
diff --git a/src/cmd/5a/lex.c b/src/cmd/5a/lex.c index a04cda220..3978f1a6c 100644 --- a/src/cmd/5a/lex.c +++ b/src/cmd/5a/lex.c @@ -338,6 +338,8 @@ struct "NRMD", LTYPEI, ANRMD, */ + "SQRTF", LTYPEI, ASQRTF, + "SQRTD", LTYPEI, ASQRTD, "CMPF", LTYPEL, ACMPF, "CMPD", LTYPEL, ACMPD, "ADDF", LTYPEK, AADDF, diff --git a/src/cmd/5g/gg.h b/src/cmd/5g/gg.h index fe404ed79..ce4558e21 100644 --- a/src/cmd/5g/gg.h +++ b/src/cmd/5g/gg.h @@ -23,6 +23,7 @@ struct Addr char sval[NSNAME]; Sym* sym; + Node* node; int width; uchar type; char name; diff --git a/src/cmd/5g/gsubr.c b/src/cmd/5g/gsubr.c index 83a9949d6..bc39912ea 100644 --- a/src/cmd/5g/gsubr.c +++ b/src/cmd/5g/gsubr.c @@ -1101,6 +1101,7 @@ naddr(Node *n, Addr *a, int canemitcode) a->type = D_NONE; a->name = D_NONE; a->reg = NREG; + a->node = N; if(n == N) return; @@ -1189,6 +1190,8 @@ naddr(Node *n, Addr *a, int canemitcode) break; case PAUTO: a->name = D_AUTO; + if (n->sym) + a->node = n->orig; break; case PPARAM: case PPARAMOUT: diff --git a/src/cmd/5g/reg.c b/src/cmd/5g/reg.c index 1cbeb3e3d..5fba02c9e 100644 --- a/src/cmd/5g/reg.c +++ b/src/cmd/5g/reg.c @@ -32,6 +32,8 @@ #include "gg.h" #include "opt.h" +#define NREGVAR 24 +#define REGBITS ((uint32)0xffffff) #define P2R(p) (Reg*)(p->reg) void addsplits(void); @@ -128,6 +130,33 @@ setaddrs(Bits bit) } } +static char* regname[] = { + ".R0", + ".R1", + ".R2", + ".R3", + ".R4", + ".R5", + ".R6", + ".R7", + ".R8", + ".R9", + ".R10", + ".R11", + ".R12", + ".R13", + ".R14", + ".R15", + ".F0", + ".F1", + ".F2", + ".F3", + ".F4", + ".F5", + ".F6", + ".F7", +}; + void regopt(Prog *firstp) { @@ -136,7 +165,7 @@ regopt(Prog *firstp) int i, z, nr; uint32 vreg; Bits bit; - + if(first == 0) { fmtinstall('Q', Qconv); } @@ -164,7 +193,17 @@ regopt(Prog *firstp) r1 = R; firstr = R; lastr = R; - nvar = 0; + + /* + * control flow is more complicated in generated go code + * than in generated c code. define pseudo-variables for + * registers, so we have complete register usage information. + */ + nvar = NREGVAR; + memset(var, 0, NREGVAR*sizeof var[0]); + for(i=0; i<NREGVAR; i++) + var[i].sym = lookup(regname[i]); + regbits = RtoB(REGSP)|RtoB(REGLINK)|RtoB(REGPC); for(z=0; z<BITS; z++) { externs.b[z] = 0; @@ -223,6 +262,16 @@ regopt(Prog *firstp) bit = mkvar(r, &p->from); for(z=0; z<BITS; z++) r->use1.b[z] |= bit.b[z]; + + /* + * middle always read when present + */ + if(p->reg != NREG) { + if(p->from.type != D_FREG) + r->use1.b[0] |= RtoB(p->reg); + else + r->use1.b[0] |= FtoB(p->reg); + } /* * right side depends on opcode @@ -233,6 +282,67 @@ regopt(Prog *firstp) default: yyerror("reg: unknown op: %A", p->as); break; + + /* + * right side read + */ + case ATST: + case ATEQ: + case ACMP: + case ACMN: + case ACMPD: + case ACMPF: + rightread: + for(z=0; z<BITS; z++) + r->use2.b[z] |= bit.b[z]; + break; + + /* + * right side read or read+write, depending on middle + * ADD x, z => z += x + * ADD x, y, z => z = x + y + */ + case AADD: + case AAND: + case AEOR: + case ASUB: + case ARSB: + case AADC: + case ASBC: + case ARSC: + case AORR: + case ABIC: + case ASLL: + case ASRL: + case ASRA: + case AMUL: + case AMULU: + case ADIV: + case AMOD: + case AMODU: + case ADIVU: + if(p->reg != NREG) + goto rightread; + // fall through + + /* + * right side read+write + */ + case AADDF: + case AADDD: + case ASUBF: + case ASUBD: + case AMULF: + case AMULD: + case ADIVF: + case ADIVD: + case AMULAL: + case AMULALU: + for(z=0; z<BITS; z++) { + r->use2.b[z] |= bit.b[z]; + r->set.b[z] |= bit.b[z]; + } + break; /* * right side write @@ -240,11 +350,22 @@ regopt(Prog *firstp) case ANOP: case AMOVB: case AMOVBU: + case AMOVD: + case AMOVDF: + case AMOVDW: + case AMOVF: + case AMOVFW: case AMOVH: case AMOVHU: case AMOVW: - case AMOVF: - case AMOVD: + case AMOVWD: + case AMOVWF: + case AMVN: + case AMULL: + case AMULLU: + if((p->scond & C_SCOND) != C_SCOND_NONE) + for(z=0; z<BITS; z++) + r->use2.b[z] |= bit.b[z]; for(z=0; z<BITS; z++) r->set.b[z] |= bit.b[z]; break; @@ -397,6 +518,24 @@ loop2: } /* + * pass 4.5 + * move register pseudo-variables into regu. + */ + for(r = firstr; r != R; r = r->link) { + r->regu = (r->refbehind.b[0] | r->set.b[0]) & REGBITS; + + r->set.b[0] &= ~REGBITS; + r->use1.b[0] &= ~REGBITS; + r->use2.b[0] &= ~REGBITS; + r->refbehind.b[0] &= ~REGBITS; + r->refahead.b[0] &= ~REGBITS; + r->calbehind.b[0] &= ~REGBITS; + r->calahead.b[0] &= ~REGBITS; + r->regdiff.b[0] &= ~REGBITS; + r->act.b[0] &= ~REGBITS; + } + + /* * pass 5 * isolate regions * calculate costs (paint1) @@ -606,6 +745,7 @@ addmove(Reg *r, int bn, int rn, int f) a = &p1->to; a->sym = v->sym; a->name = v->name; + a->node = v->node; a->offset = v->offset; a->etype = v->etype; a->type = D_OREG; @@ -715,21 +855,40 @@ mkvar(Reg *r, Adr *a) goto onereg; case D_REGREG: + bit = zbits; if(a->offset != NREG) - r->regu |= RtoB(a->offset); - goto onereg; + bit.b[0] |= RtoB(a->offset); + if(a->reg != NREG) + bit.b[0] |= RtoB(a->reg); + return bit; case D_REG: case D_SHIFT: - case D_OREG: onereg: - if(a->reg != NREG) - r->regu |= RtoB(a->reg); + if(a->reg != NREG) { + bit = zbits; + bit.b[0] = RtoB(a->reg); + return bit; + } + break; + + case D_OREG: + if(a->reg != NREG) { + if(a == &r->prog->from) + r->use1.b[0] |= RtoB(a->reg); + else + r->use2.b[0] |= RtoB(a->reg); + if(r->prog->scond & (C_PBIT|C_WBIT)) + r->set.b[0] |= RtoB(a->reg); + } break; case D_FREG: - if(a->reg != NREG) - r->regu |= FtoB(a->reg); + if(a->reg != NREG) { + bit = zbits; + bit.b[0] = FtoB(a->reg); + return bit; + } break; } @@ -795,7 +954,8 @@ mkvar(Reg *r, Adr *a) v->etype = et; v->width = w; v->addr = flag; // funny punning - + v->node = a->node; + if(debug['R']) print("bit=%2d et=%E pun=%d %D\n", i, et, flag, a); diff --git a/src/cmd/5l/5.out.h b/src/cmd/5l/5.out.h index 002b46d45..cf86ae48b 100644 --- a/src/cmd/5l/5.out.h +++ b/src/cmd/5l/5.out.h @@ -53,8 +53,6 @@ #define REGLINK 14 #define REGPC 15 -#define REGTMPT 7 /* used by the loader for thumb code */ - #define NFREG 8 #define FREGRET 0 #define FREGEXT 7 @@ -126,6 +124,8 @@ enum as AMULD, ADIVF, ADIVD, + ASQRTF, + ASQRTD, ASRL, ASRA, diff --git a/src/cmd/5l/Makefile b/src/cmd/5l/Makefile index c11ebe990..9f4a192aa 100644 --- a/src/cmd/5l/Makefile +++ b/src/cmd/5l/Makefile @@ -22,7 +22,6 @@ OFILES=\ optab.$O\ pass.$O\ prof.$O\ - thumb.$O\ softfloat.$O\ span.$O\ symtab.$O\ diff --git a/src/cmd/5l/asm.c b/src/cmd/5l/asm.c index 81c08e353..4afed2b80 100644 --- a/src/cmd/5l/asm.c +++ b/src/cmd/5l/asm.c @@ -34,8 +34,6 @@ #include "../ld/lib.h" #include "../ld/elf.h" -int32 OFFSET; - static Prog *PP; char linuxdynld[] = "/lib/ld-linux.so.2"; @@ -295,7 +293,7 @@ asmb(void) { int32 t; int a, dynsym; - uint32 fo, symo, startva, elfsymo, elfstro, elfsymsize; + uint32 fo, symo, startva; ElfEhdr *eh; ElfPhdr *ph, *pph; ElfShdr *sh; @@ -305,10 +303,6 @@ asmb(void) Bprint(&bso, "%5.2f asmb\n", cputime()); Bflush(&bso); - elfsymsize = 0; - elfstro = 0; - elfsymo = 0; - sect = segtext.sect; seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0); codeblk(sect->vaddr, sect->len); @@ -361,34 +355,28 @@ asmb(void) debug['s'] = 1; break; case Hplan9x32: - OFFSET = HEADR+textsize+segdata.filelen; - seek(cout, OFFSET, 0); + symo = HEADR+segtext.len+segdata.filelen; break; case Hnetbsd: - OFFSET += rnd(segdata.filelen, 4096); - seek(cout, OFFSET, 0); + symo = rnd(segdata.filelen, 4096); break; ElfSym: symo = rnd(HEADR+segtext.filelen, INITRND)+segdata.filelen; symo = rnd(symo, INITRND); break; } + seek(cout, symo, 0); if(iself) { if(debug['v']) Bprint(&bso, "%5.2f elfsym\n", cputime()); - elfsymo = symo+8+symsize+lcsize; - seek(cout, elfsymo, 0); - asmelfsym32(); + asmelfsym(); cflush(); - elfstro = seek(cout, 0, 1); - elfsymsize = elfstro - elfsymo; ewrite(cout, elfstrdat, elfstrsize); // if(debug['v']) // Bprint(&bso, "%5.2f dwarf\n", cputime()); // dwarfemitdebugsections(); } - asmthumbmap(); cflush(); } @@ -397,8 +385,7 @@ asmb(void) if(debug['v']) Bprint(&bso, "%5.2f header\n", cputime()); Bflush(&bso); - OFFSET = 0; - seek(cout, OFFSET, 0); + seek(cout, 0L, 0); switch(HEADTYPE) { case Hnoheader: /* no header */ break; @@ -599,15 +586,15 @@ asmb(void) sh = newElfShdr(elfstr[ElfStrSymtab]); sh->type = SHT_SYMTAB; - sh->off = elfsymo; - sh->size = elfsymsize; + sh->off = symo; + sh->size = symsize; sh->addralign = 4; sh->entsize = 16; sh->link = eh->shnum; // link to strtab sh = newElfShdr(elfstr[ElfStrStrtab]); sh->type = SHT_STRTAB; - sh->off = elfstro; + sh->off = symo+symsize; sh->size = elfstrsize; sh->addralign = 1; @@ -740,59 +727,6 @@ nopstat(char *f, Count *c) (double)(c->outof - c->count)/c->outof); } -static void -outt(int32 f, int32 l) -{ - if(debug['L']) - Bprint(&bso, "tmap: %ux-%ux\n", f, l); - lput(f); - lput(l); -} - -void -asmthumbmap(void) -{ - int32 pc, lastt; - Prog *p; - - if(!seenthumb) - return; - pc = 0; - lastt = -1; - for(cursym = textp; cursym != nil; cursym = cursym->next) { - p = cursym->text; - pc = p->pc - INITTEXT; - setarch(p); - if(thumb){ - if(p->from.sym->foreign){ // 8 bytes of ARM first - if(lastt >= 0){ - outt(lastt, pc-1); - lastt = -1; - } - pc += 8; - } - if(lastt < 0) - lastt = pc; - } - else{ - if(p->from.sym->foreign){ // 4 bytes of THUMB first - if(lastt < 0) - lastt = pc; - pc += 4; - } - if(lastt >= 0){ - outt(lastt, pc-1); - lastt = -1; - } - } - if(cursym->next == nil) - for(; p != P; p = p->link) - pc = p->pc = INITTEXT; - } - if(lastt >= 0) - outt(lastt, pc+1); -} - void asmout(Prog *p, Optab *o, int32 *out) { @@ -816,7 +750,7 @@ if(debug['P']) print("%ux: %P type %d\n", (uint32)(p->pc), p, o->type); break; case 0: /* pseudo ops */ -if(debug['G']) print("%ux: %s: arm %d %d %d\n", (uint32)(p->pc), p->from.sym->name, p->from.sym->thumb, p->from.sym->foreign, p->from.sym->fnptr); +if(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 */ @@ -880,10 +814,6 @@ if(debug['G']) print("%ux: %s: arm %d %d %d\n", (uint32)(p->pc), p->from.sym->na v = -8; if(p->cond != P) v = (p->cond->pc - pc) - 8; -#ifdef CALLEEBX - if(p->as == ABL) - v += fninc(p->to.sym); -#endif o1 = opbra(p->as, p->scond); o1 |= (v >> 2) & 0xffffff; break; @@ -1198,7 +1128,7 @@ if(debug['G']) print("%ux: %s: arm %d %d %d\n", (uint32)(p->pc), p->from.sym->na r = p->reg; if(r == NREG) { r = rt; - if(p->as == AMOVF || p->as == AMOVD) + if(p->as == AMOVF || p->as == AMOVD || p->as == ASQRTF || p->as == ASQRTD) r = 0; } o1 |= rf | (r<<16) | (rt<<12); @@ -1340,19 +1270,7 @@ if(debug['G']) print("%ux: %s: arm %d %d %d\n", (uint32)(p->pc), p->from.sym->na o2 ^= (1<<6); break; case 74: /* bx $I */ -#ifdef CALLEEBX - diag("bx $i case (arm)"); -#endif - if(!seenthumb) - diag("ABX $I and seenthumb==0"); - v = p->cond->pc; - if(p->to.sym->thumb) - v |= 1; // T bit - o1 = olr(8, REGPC, REGTMP, p->scond&C_SCOND); // mov 8(PC), Rtmp - o2 = oprrr(AADD, p->scond) | immrot(8) | (REGPC<<16) | (REGLINK<<12); // add 8,PC, LR - o3 = ((p->scond&C_SCOND)<<28) | (0x12fff<<8) | (1<<4) | REGTMP; // bx Rtmp - o4 = opbra(AB, 14); // B over o6 - o5 = v; + diag("ABX $I"); break; case 75: /* bx O(R) */ aclass(&p->to); @@ -1371,14 +1289,7 @@ if(debug['G']) print("%ux: %s: arm %d %d %d\n", (uint32)(p->pc), p->from.sym->na o3 = ((p->scond&C_SCOND)<<28) | (0x12fff<<8) | (1<<4) | REGTMP; // BX Rtmp break; case 76: /* bx O(R) when returning from fn*/ - if(!seenthumb) - diag("ABXRET and seenthumb==0"); - aclass(&p->to); -// print("ARM BXRET %d(R%d)\n", instoffset, p->to.reg); - if(instoffset != 0) - diag("non-zero offset in ABXRET"); - // o1 = olr(instoffset, p->to.reg, REGTMP, p->scond); // mov O(R), Rtmp - o1 = ((p->scond&C_SCOND)<<28) | (0x12fff<<8) | (1<<4) | p->to.reg; // BX R + diag("ABXRET"); break; case 77: /* ldrex oreg,reg */ aclass(&p->from); @@ -1632,6 +1543,8 @@ oprrr(int a, int sc) 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 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); diff --git a/src/cmd/5l/l.h b/src/cmd/5l/l.h index f3c9d839d..182f3e738 100644 --- a/src/cmd/5l/l.h +++ b/src/cmd/5l/l.h @@ -144,8 +144,6 @@ struct Sym int32 sig; int32 size; uchar special; - uchar thumb; // thumb code - uchar foreign; // called by arm if thumb, by thumb if arm uchar fnptr; // used as fn ptr Sym* hash; // in hash table Sym* allsym; // in all symbol list @@ -208,7 +206,6 @@ enum LFROM = 1<<0, LTO = 1<<1, LPOOL = 1<<2, - V4 = 1<<3, /* arm v4 arch */ C_NONE = 0, C_REG, @@ -221,21 +218,16 @@ enum C_RCON, /* 0xff rotated */ C_NCON, /* ~RCON */ C_SCON, /* 0xffff */ - C_BCON, /* thumb */ C_LCON, C_ZFCON, C_SFCON, C_LFCON, - C_GCON, /* thumb */ C_RACON, - C_SACON, /* thumb */ C_LACON, - C_GACON, /* thumb */ C_SBRA, C_LBRA, - C_GBRA, /* thumb */ C_HAUTO, /* halfword insn offset (-0xff to 0xff) */ C_FAUTO, /* float insn offset (0 to 0x3fc, word aligned) */ @@ -250,12 +242,10 @@ enum C_ROREG, C_SROREG, /* both S and R */ C_LOREG, - C_GOREG, /* thumb */ C_PC, C_SP, C_HREG, - C_OFFPC, /* thumb */ C_ADDR, /* reference to relocatable address */ @@ -287,9 +277,6 @@ EXTERN union #define cbuf u.obuf #define xbuf u.ibuf -#define setarch(p) if((p)->as==ATEXT) thumb=(p)->reg&ALLTHUMBS -#define setthumb(p) if((p)->as==ATEXT) seenthumb|=(p)->reg&ALLTHUMBS - #ifndef COFFCVT EXTERN int32 HEADR; /* length of header */ @@ -319,7 +306,6 @@ EXTERN int nerrors; EXTERN int32 instoffset; EXTERN Opcross opcross[8]; EXTERN Oprang oprange[ALAST]; -EXTERN Oprang thumboprange[ALAST]; EXTERN char* outfile; EXTERN int32 pc; EXTERN uchar repop[ALAST]; @@ -333,14 +319,10 @@ EXTERN int version; EXTERN char xcmp[C_GOK+1][C_GOK+1]; EXTERN Prog zprg; EXTERN int dtype; -EXTERN int armv4; -EXTERN int thumb; -EXTERN int seenthumb; EXTERN int armsize; extern char* anames[]; extern Optab optab[]; -extern Optab thumboptab[]; void addpool(Prog*, Adr*); EXTERN Prog* blitrl; @@ -368,17 +350,13 @@ int Oconv(Fmt*); int Pconv(Fmt*); int Sconv(Fmt*); int aclass(Adr*); -int thumbaclass(Adr*, Prog*); void addhist(int32, int); Prog* appendp(Prog*); void asmb(void); -void asmthumbmap(void); void asmout(Prog*, Optab*, int32*); -void thumbasmout(Prog*, Optab*); int32 atolwhex(char*); Prog* brloop(Prog*); void buildop(void); -void thumbbuildop(void); void buildrep(int, int); void cflush(void); int chipzero(Ieee*); @@ -442,9 +420,6 @@ int32 immaddr(int32); int32 opbra(int, int); int brextra(Prog*); int isbranch(Prog*); -int fnpinc(Sym *); -int fninc(Sym *); -void thumbcount(void); void fnptrs(void); void doelf(void); diff --git a/src/cmd/5l/list.c b/src/cmd/5l/list.c index 2ae25d491..fa838215b 100644 --- a/src/cmd/5l/list.c +++ b/src/cmd/5l/list.c @@ -412,7 +412,6 @@ static char* cnames[] = { [C_ADDR] = "C_ADDR", - [C_BCON] = "C_BCON", [C_FAUTO] = "C_FAUTO", [C_ZFCON] = "C_SFCON", [C_SFCON] = "C_SFCON", @@ -420,11 +419,7 @@ cnames[] = [C_FCR] = "C_FCR", [C_FOREG] = "C_FOREG", [C_FREG] = "C_FREG", - [C_GACON] = "C_GACON", - [C_GBRA] = "C_GBRA", - [C_GCON] = "C_GCON", [C_GOK] = "C_GOK", - [C_GOREG] = "C_GOREG", [C_HAUTO] = "C_HAUTO", [C_HFAUTO] = "C_HFAUTO", [C_HFOREG] = "C_HFOREG", @@ -437,7 +432,6 @@ cnames[] = [C_LOREG] = "C_LOREG", [C_NCON] = "C_NCON", [C_NONE] = "C_NONE", - [C_OFFPC] = "C_OFFPC", [C_PC] = "C_PC", [C_PSR] = "C_PSR", [C_RACON] = "C_RACON", @@ -445,7 +439,6 @@ cnames[] = [C_REG] = "C_REG", [C_REGREG] = "C_REGREG", [C_ROREG] = "C_ROREG", - [C_SACON] = "C_SACON", [C_SAUTO] = "C_SAUTO", [C_SBRA] = "C_SBRA", [C_SCON] = "C_SCON", diff --git a/src/cmd/5l/noop.c b/src/cmd/5l/noop.c index bdcc3cad8..e7c2db5f2 100644 --- a/src/cmd/5l/noop.c +++ b/src/cmd/5l/noop.c @@ -47,74 +47,11 @@ static Sym* sym_modu; static void setdiv(int); -static Prog * -movrr(Prog *q, int rs, int rd, Prog *p) -{ - if(q == nil) - q = prg(); - q->as = AMOVW; - q->line = p->line; - q->from.type = D_REG; - q->from.reg = rs; - q->to.type = D_REG; - q->to.reg = rd; - q->link = p->link; - return q; -} - -static Prog * -fnret(Prog *q, int rs, int foreign, Prog *p) -{ - q = movrr(q, rs, REGPC, p); - if(foreign){ // BX rs - q->as = ABXRET; - q->from.type = D_NONE; - q->from.reg = NREG; - q->to.reg = rs; - } - return q; -} - -static Prog * -aword(int32 w, Prog *p) -{ - Prog *q; - - q = prg(); - q->as = AWORD; - q->line = p->line; - q->from.type = D_NONE; - q->reg = NREG; - q->to.type = D_CONST; - q->to.offset = w; - q->link = p->link; - p->link = q; - return q; -} - -static Prog * -adword(int32 w1, int32 w2, Prog *p) -{ - Prog *q; - - q = prg(); - q->as = ADWORD; - q->line = p->line; - q->from.type = D_CONST; - q->from.offset = w1; - q->reg = NREG; - q->to.type = D_CONST; - q->to.offset = w2; - q->link = p->link; - p->link = q; - return q; -} - void noops(void) { - Prog *p, *q, *q1, *q2; - int o, foreign; + Prog *p, *q, *q1; + int o; Prog *pmorestack; Sym *symmorestack; @@ -140,8 +77,6 @@ noops(void) q = P; for(cursym = textp; cursym != nil; cursym = cursym->next) { for(p = cursym->text; p != P; p = p->link) { - setarch(p); - switch(p->as) { case ATEXT: p->mark |= LEAF; @@ -206,7 +141,6 @@ noops(void) for(cursym = textp; cursym != nil; cursym = cursym->next) { for(p = cursym->text; p != P; p = p->link) { - setarch(p); o = p->as; switch(o) { case ATEXT: @@ -224,54 +158,12 @@ noops(void) Bflush(&bso); cursym->text->mark |= LEAF; } -#ifdef CALLEEBX - if(p->from.sym->foreign){ - if(thumb) - // don't allow literal pool to separate these - p = adword(0xe28f7001, 0xe12fff17, p); // arm add 1, pc, r7 and bx r7 - // p = aword(0xe12fff17, aword(0xe28f7001, p)); // arm add 1, pc, r7 and bx r7 - else - p = aword(0x4778, p); // thumb bx pc and 2 bytes padding - } -#endif if(cursym->text->mark & LEAF) { cursym->leaf = 1; if(!autosize) break; } - if(thumb){ - if(!(p->reg & NOSPLIT)) - diag("stack splitting not supported in thumb"); - if(!(cursym->text->mark & LEAF)){ - q = movrr(nil, REGLINK, REGTMPT-1, p); - p->link = q; - q1 = prg(); - q1->as = AMOVW; - q1->line = p->line; - q1->from.type = D_REG; - q1->from.reg = REGTMPT-1; - q1->to.type = D_OREG; - q1->to.name = D_NONE; - q1->to.reg = REGSP; - q1->to.offset = 0; - q1->link = q->link; - q->link = q1; - } - if(autosize){ - q2 = prg(); - q2->as = ASUB; - q2->line = p->line; - q2->from.type = D_CONST; - q2->from.offset = autosize; - q2->to.type = D_REG; - q2->to.reg = REGSP; - q2->link = p->link; - p->link = q2; - } - break; - } - if(p->reg & NOSPLIT) { q1 = prg(); q1->as = AMOVW; @@ -432,16 +324,9 @@ noops(void) case ARET: nocache(p); - foreign = seenthumb && (cursym->foreign || cursym->fnptr); -// print("%s %d %d\n", cursym->name, cursym->foreign, cursym->fnptr); if(cursym->text->mark & LEAF) { if(!autosize) { - if(thumb){ - p = fnret(p, REGLINK, foreign, p); - break; - } -// if(foreign) print("ABXRET 1 %s\n", cursym->name); - p->as = foreign ? ABXRET : AB; + p->as = AB; p->from = zprg.from; p->to.type = D_OREG; p->to.offset = 0; @@ -449,95 +334,16 @@ noops(void) break; } } - if(thumb){ - diag("thumb not maintained"); - errorexit(); - if(cursym->text->mark & LEAF){ - if(autosize){ - p->as = AADD; - p->from.type = D_CONST; - p->from.offset = autosize; - p->to.type = D_REG; - p->to.reg = REGSP; - q = nil; - } - else - q = p; - q = fnret(q, REGLINK, foreign, p); - if(q != p) - p->link = q; - } - else{ - p->as = AMOVW; - p->from.type = D_OREG; - p->from.name = D_NONE; - p->from.reg = REGSP; - p->from.offset = 0; - p->to.type = D_REG; - p->to.reg = REGTMPT-1; - if(autosize){ - q = prg(); - q->as = AADD; - q->from.type = D_CONST; - q->from.offset = autosize; - q->to.type = D_REG; - q->to.reg = REGSP; - q->link = p->link; - p->link = q; - } - else - q = p; - q1 = fnret(nil, REGTMPT-1, foreign, p); - q1->link = q->link; - q->link = q1; - } - break; - } - if(foreign) { - diag("foreign not maintained"); - errorexit(); -// if(foreign) print("ABXRET 3 %s\n", cursym->name); -#define R 1 - p->as = AMOVW; - p->from.type = D_OREG; - p->from.name = D_NONE; - p->from.reg = REGSP; - p->from.offset = 0; - p->to.type = D_REG; - p->to.reg = R; - q = prg(); - q->as = AADD; - q->scond = p->scond; - q->line = p->line; - q->from.type = D_CONST; - q->from.offset = autosize; - q->to.type = D_REG; - q->to.reg = REGSP; - q->link = p->link; - p->link = q; - q1 = prg(); - q1->as = ABXRET; - q1->scond = p->scond; - q1->line = p->line; - q1->to.type = D_OREG; - q1->to.offset = 0; - q1->to.reg = R; - q1->link = q->link; - q->link = q1; -#undef R - } - else { - p->as = AMOVW; - p->scond |= C_PBIT; - p->from.type = D_OREG; - p->from.offset = autosize; - p->from.reg = REGSP; - p->to.type = D_REG; - p->to.reg = REGPC; - // If there are instructions following - // this ARET, they come from a branch - // with the same stackframe, so no spadj. - } + p->as = AMOVW; + p->scond |= C_PBIT; + p->from.type = D_OREG; + p->from.offset = autosize; + p->from.reg = REGSP; + p->to.type = D_REG; + p->to.reg = REGPC; + // If there are instructions following + // this ARET, they come from a branch + // with the same stackframe, so no spadj. break; case AADD: @@ -589,7 +395,7 @@ noops(void) if(q1->reg == NREG) p->from.reg = q1->to.reg; p->to.type = D_REG; - p->to.reg = prog_div->from.sym->thumb ? REGTMPT : REGTMP; + p->to.reg = REGTMP; p->to.offset = 0; /* CALL appropriate */ @@ -598,14 +404,7 @@ noops(void) p->link = q; p = q; -#ifdef CALLEEBX p->as = ABL; -#else - if(prog_div->from.sym->thumb) - p->as = thumb ? ABL : ABX; - else - p->as = thumb ? ABX : ABL; -#endif p->line = q1->line; p->to.type = D_BRANCH; p->cond = p; @@ -637,7 +436,7 @@ noops(void) p->as = AMOVW; p->line = q1->line; p->from.type = D_REG; - p->from.reg = prog_div->from.sym->thumb ? REGTMPT : REGTMP; + p->from.reg = REGTMP; p->from.offset = 0; p->to.type = D_REG; p->to.reg = q1->to.reg; @@ -669,12 +468,6 @@ noops(void) break; case AMOVW: - if(thumb){ - Adr *a = &p->from; - - if(a->type == D_CONST && ((a->name == D_NONE && a->reg == REGSP) || a->name == D_AUTO || a->name == D_PARAM) && (a->offset & 3)) - diag("SP offset not multiple of 4"); - } if((p->scond & C_WBIT) && p->to.type == D_OREG && p->to.reg == REGSP) p->spadj = -p->to.offset; if((p->scond & C_PBIT) && p->from.type == D_OREG && p->from.reg == REGSP && p->to.reg != REGPC) @@ -682,136 +475,6 @@ noops(void) if(p->from.type == D_CONST && p->from.reg == REGSP && p->to.type == D_REG && p->to.reg == REGSP) p->spadj = -p->from.offset; break; - case AMOVB: - case AMOVBU: - case AMOVH: - case AMOVHU: - if(thumb){ - if(p->from.type == D_OREG && (p->from.name == D_AUTO || p->from.name == D_PARAM || (p->from.name == D_CONST && p->from.reg == REGSP))){ - q = prg(); - *q = *p; - if(p->from.name == D_AUTO) - q->from.offset += autosize; - else if(p->from.name == D_PARAM) - q->from.offset += autosize+4; - q->from.name = D_NONE; - q->from.reg = REGTMPT; - p = movrr(p, REGSP, REGTMPT, p); - q->link = p->link; - p->link = q; - } - if(p->to.type == D_OREG && (p->to.name == D_AUTO || p->to.name == D_PARAM || (p->to.name == D_CONST && p->to.reg == REGSP))){ - q = prg(); - *q = *p; - if(p->to.name == D_AUTO) - q->to.offset += autosize; - else if(p->to.name == D_PARAM) - q->to.offset += autosize+4; - q->to.name = D_NONE; - q->to.reg = REGTMPT; - p = movrr(p, REGSP, REGTMPT, p); - q->link = p->link; - p->link = q; - if(q->to.offset < 0 || q->to.offset > 255){ // complicated - p->to.reg = REGTMPT+1; // mov sp, r8 - q1 = prg(); - q1->line = p->line; - q1->as = AMOVW; - q1->from.type = D_CONST; - q1->from.offset = q->to.offset; - q1->to.type = D_REG; - q1->to.reg = REGTMPT; // mov $o, r7 - p->link = q1; - q1->link = q; - q1 = prg(); - q1->line = p->line; - q1->as = AADD; - q1->from.type = D_REG; - q1->from.reg = REGTMPT+1; - q1->to.type = D_REG; - q1->to.reg = REGTMPT; // add r8, r7 - p->link->link = q1; - q1->link = q; - q->to.offset = 0; // mov* r, 0(r7) - /* phew */ - } - } - } - break; - case AMOVM: - if(thumb){ - if(p->from.type == D_OREG){ - if(p->from.offset == 0) - p->from.type = D_REG; - else - diag("non-zero AMOVM offset"); - } - else if(p->to.type == D_OREG){ - if(p->to.offset == 0) - p->to.type = D_REG; - else - diag("non-zero AMOVM offset"); - } - } - break; - case AB: - if(thumb && p->to.type == D_OREG){ - if(p->to.offset == 0){ - p->as = AMOVW; - p->from.type = D_REG; - p->from.reg = p->to.reg; - p->to.type = D_REG; - p->to.reg = REGPC; - } - else{ - p->as = AADD; - p->from.type = D_CONST; - p->from.offset = p->to.offset; - p->reg = p->to.reg; - p->to.type = D_REG; - p->to.reg = REGTMPT-1; - q = prg(); - q->as = AMOVW; - q->line = p->line; - q->from.type = D_REG; - q->from.reg = REGTMPT-1; - q->to.type = D_REG; - q->to.reg = REGPC; - q->link = p->link; - p->link = q; - } - } - if(seenthumb && !thumb && p->to.type == D_OREG && p->to.reg == REGLINK){ - // print("warn %s: b (R%d) assuming a return\n", cursym->name, p->to.reg); - p->as = ABXRET; - } - break; - case ABL: - case ABX: - if(thumb && p->to.type == D_OREG){ - if(p->to.offset == 0){ - p->as = o; - p->from.type = D_NONE; - p->to.type = D_REG; - } - else{ - p->as = AADD; - p->from.type = D_CONST; - p->from.offset = p->to.offset; - p->reg = p->to.reg; - p->to.type = D_REG; - p->to.reg = REGTMPT-1; - q = prg(); - q->as = o; - q->line = p->line; - q->from.type = D_NONE; - q->to.type = D_REG; - q->to.reg = REGTMPT-1; - q->link = p->link; - p->link = q; - } - } - break; } } } @@ -876,13 +539,19 @@ setdiv(int as) Prog *p = nil; switch(as){ - case ADIV: p = prog_div; break; - case ADIVU: p = prog_divu; break; - case AMOD: p = prog_mod; break; - case AMODU: p = prog_modu; break; + case ADIV: + p = prog_div; + break; + case ADIVU: + p = prog_divu; + break; + case AMOD: + p = prog_mod; + break; + case AMODU: + p = prog_modu; + break; } - if(thumb != p->from.sym->thumb) - p->from.sym->foreign = 1; } void diff --git a/src/cmd/5l/obj.c b/src/cmd/5l/obj.c index 96ba0010f..dd3a7329a 100644 --- a/src/cmd/5l/obj.c +++ b/src/cmd/5l/obj.c @@ -67,6 +67,7 @@ static char* linkername[] = { "runtime.softfloat", + "math.sqrtGoC", }; void @@ -241,7 +242,6 @@ main(int argc, char *argv[]) zprg.from.reg = NREG; zprg.to = zprg.from; buildop(); - thumbbuildop(); // could build on demand histgen = 0; pc = 0; dtype = 4; @@ -285,10 +285,8 @@ main(int argc, char *argv[]) asmb(); undef(); - if(debug['c']){ - thumbcount(); + if(debug['c']) print("ARM size = %d\n", armsize); - } if(debug['v']) { Bprint(&bso, "%5.2f cpu time\n", cputime()); Bprint(&bso, "%d sizeof adr\n", sizeof(Adr)); @@ -405,8 +403,6 @@ nopout(Prog *p) p->to.type = D_NONE; } -static void puntfp(Prog *); - void ldobj1(Biobuf *f, char *pkg, int64 len, char *pn) { @@ -621,8 +617,6 @@ loop: else textp = s; etextp = s; - setarch(p); - setthumb(p); p->align = 4; autosize = (p->to.offset+3L) & ~3L; p->to.offset = autosize; @@ -630,7 +624,6 @@ loop: s->type = STEXT; s->text = p; s->value = pc; - s->thumb = thumb; lastp = p; p->pc = pc; pc++; @@ -672,13 +665,9 @@ loop: case AMULD: case ADIVF: case ADIVD: - if(thumb) - puntfp(p); goto casedef; case AMOVF: - if(thumb) - puntfp(p); if(skip) goto casedef; @@ -700,8 +689,6 @@ loop: goto casedef; case AMOVD: - if(thumb) - puntfp(p); if(skip) goto casedef; @@ -757,17 +744,6 @@ prg(void) return p; } -static void -puntfp(Prog *p) -{ - USED(p); - /* floating point - punt for now */ - cursym->text->reg = NREG; /* ARM */ - cursym->thumb = 0; - thumb = 0; - // print("%s: generating ARM code (contains floating point ops %d)\n", curtext->from.sym->name, p->line); -} - Prog* appendp(Prog *q) { diff --git a/src/cmd/5l/optab.c b/src/cmd/5l/optab.c index 4816aa40f..514786f85 100644 --- a/src/cmd/5l/optab.c +++ b/src/cmd/5l/optab.c @@ -77,7 +77,6 @@ Optab optab[] = { 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_GCON, 11, 4, 0 }, { AWORD, C_NONE, C_NONE, C_ADDR, 11, 4, 0 }, { AMOVW, C_NCON, C_NONE, C_REG, 12, 4, 0 }, @@ -181,34 +180,34 @@ Optab optab[] = { ACASE, C_REG, C_NONE, C_NONE, 62, 4, 0 }, { ABCASE, C_NONE, C_NONE, C_SBRA, 63, 4, 0 }, - { AMOVH, C_REG, C_NONE, C_HAUTO, 70, 4, REGSP, V4 }, - { AMOVH, C_REG, C_NONE, C_HOREG, 70, 4, 0, V4 }, - { AMOVHU, C_REG, C_NONE, C_HAUTO, 70, 4, REGSP, V4 }, - { AMOVHU, C_REG, C_NONE, C_HOREG, 70, 4, 0, V4 }, - - { AMOVB, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, V4 }, - { AMOVB, C_HOREG,C_NONE, C_REG, 71, 4, 0, V4 }, - { AMOVH, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, V4 }, - { AMOVH, C_HOREG,C_NONE, C_REG, 71, 4, 0, V4 }, - { AMOVHU, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, V4 }, - { AMOVHU, C_HOREG,C_NONE, C_REG, 71, 4, 0, V4 }, - - { AMOVH, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO|V4 }, - { AMOVH, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO|V4 }, - { AMOVH, C_REG, C_NONE, C_ADDR, 94, 8, 0, LTO|V4 }, - { AMOVHU, C_REG, C_NONE, C_LAUTO, 72, 8, REGSP, LTO|V4 }, - { AMOVHU, C_REG, C_NONE, C_LOREG, 72, 8, 0, LTO|V4 }, - { AMOVHU, C_REG, C_NONE, C_ADDR, 94, 8, 0, LTO|V4 }, - - { AMOVB, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM|V4 }, - { AMOVB, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM|V4 }, - { AMOVB, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM|V4 }, - { AMOVH, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM|V4 }, - { AMOVH, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM|V4 }, - { AMOVH, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM|V4 }, - { AMOVHU, C_LAUTO,C_NONE, C_REG, 73, 8, REGSP, LFROM|V4 }, - { AMOVHU, C_LOREG,C_NONE, C_REG, 73, 8, 0, LFROM|V4 }, - { AMOVHU, C_ADDR, C_NONE, C_REG, 93, 8, 0, LFROM|V4 }, + { AMOVH, C_REG, C_NONE, C_HAUTO, 70, 4, REGSP, 0 }, + { AMOVH, 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 }, + { AMOVH, C_HAUTO,C_NONE, C_REG, 71, 4, REGSP, 0 }, + { AMOVH, 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 }, + { 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 }, + + { 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 }, + { 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 }, + { 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 }, { ALDREX, C_SOREG,C_NONE, C_REG, 77, 4, 0 }, { ASTREX, C_SOREG,C_REG, C_REG, 78, 4, 0 }, diff --git a/src/cmd/5l/pass.c b/src/cmd/5l/pass.c index 7e1ba6a09..194a1ed5f 100644 --- a/src/cmd/5l/pass.c +++ b/src/cmd/5l/pass.c @@ -100,7 +100,6 @@ xfol(Prog *p, Prog **last) loop: if(p == P) return; - setarch(p); a = p->as; if(a == AB) { q = p->cond; @@ -210,14 +209,7 @@ patch(void) vexit = s->value; for(cursym = textp; cursym != nil; cursym = cursym->next) { for(p = cursym->text; p != P; p = p->link) { - setarch(p); a = p->as; - if(seenthumb && a == ABL){ - // if((s = p->to.sym) != S && (s1 = curtext->from.sym) != S) - // print("%s calls %s\n", s1->name, s->name); - if((s = p->to.sym) != S && s->thumb != cursym->thumb) - s->foreign = 1; - } if((a == ABL || a == ABX || a == AB || a == ARET) && p->to.type != D_BRANCH && p->to.sym != S) { s = p->to.sym; @@ -254,19 +246,7 @@ patch(void) for(cursym = textp; cursym != nil; cursym = cursym->next) { for(p = cursym->text; p != P; p = p->link) { - setarch(p); a = p->as; - if(seenthumb && a == ABL) { -#ifdef CALLEEBX - if(0) - {} -#else - if((s = p->to.sym) != S && (s->foreign || s->fnptr)) - p->as = ABX; -#endif - else if(p->to.type == D_OREG) - p->as = ABX; - } if(p->cond != P) { p->cond = brloop(p->cond); if(p->cond != P) diff --git a/src/cmd/5l/prof.c b/src/cmd/5l/prof.c index ad115a8ca..48ad2dc59 100644 --- a/src/cmd/5l/prof.c +++ b/src/cmd/5l/prof.c @@ -47,7 +47,6 @@ doprof1(void) s = lookup("__mcount", 0); n = 1; for(p = firstp->link; p != P; p = p->link) { - setarch(p); if(p->as == ATEXT) { q = prg(); q->line = p->line; @@ -74,7 +73,7 @@ doprof1(void) p->from.sym = s; p->from.offset = n*4 + 4; p->to.type = D_REG; - p->to.reg = thumb ? REGTMPT : REGTMP; + p->to.reg = REGTMP; q = prg(); q->line = p->line; @@ -86,7 +85,7 @@ doprof1(void) p->from.type = D_CONST; p->from.offset = 1; p->to.type = D_REG; - p->to.reg = thumb ? REGTMPT : REGTMP; + p->to.reg = REGTMP; q = prg(); q->line = p->line; @@ -96,7 +95,7 @@ doprof1(void) p = q; p->as = AMOVW; p->from.type = D_REG; - p->from.reg = thumb ? REGTMPT : REGTMP; + p->from.reg = REGTMP; p->to.type = D_OREG; p->to.name = D_EXTERN; p->to.sym = s; @@ -143,7 +142,6 @@ doprof2(void) ps4 = P; for(cursym = textp; cursym != nil; cursym = cursym->next) { p = cursym->text; - setarch(p); if(cursym == s2) { ps2 = p; p->reg = 1; @@ -155,7 +153,6 @@ doprof2(void) } for(cursym = textp; cursym != nil; cursym = cursym->next) for(p = cursym->text; p != P; p = p->link) { - setarch(p); if(p->as == ATEXT) { if(p->reg & NOPROF) { for(;;) { diff --git a/src/cmd/5l/softfloat.c b/src/cmd/5l/softfloat.c index 03d8c6d26..4f799d17e 100644 --- a/src/cmd/5l/softfloat.c +++ b/src/cmd/5l/softfloat.c @@ -54,6 +54,8 @@ softfloat(void) case AMULD: case ADIVF: case ADIVD: + case ASQRTF: + case ASQRTD: goto soft; default: diff --git a/src/cmd/5l/span.c b/src/cmd/5l/span.c index 4067f1a32..eb79f6b5a 100644 --- a/src/cmd/5l/span.c +++ b/src/cmd/5l/span.c @@ -50,86 +50,6 @@ isbranch(Prog *p) } static int -ispad(Prog *p) -{ - if(p->as != AMOVW) - return 0; - if(p->from.type != D_REG || p->from.reg != REGTMP) - return 0; - if(p->to.type != D_REG || p->to.reg != REGTMP) - return 0; - return 1; -} - -int -fninc(Sym *s) -{ - if(thumb){ - if(s->thumb){ - if(s->foreign) - return 8; - else - return 0; - } - else{ - if(s->foreign) - return 0; - else - diag("T A !foreign in fninc"); - } - } - else{ - if(s->thumb){ - if(s->foreign) - return 0; - else - diag("A T !foreign in fninc"); - } - else{ - if(s->foreign) - return 4; - else - return 0; - } - } - return 0; -} - -int -fnpinc(Sym *s) -{ - if(!s->fnptr){ // a simplified case BX O(R) -> BL O(R) - if(!debug['f']) - diag("fnptr == 0 in fnpinc"); - if(s->foreign) - diag("bad usage in fnpinc %s %d %d", s->name, s->foreign, s->thumb); - return 0; - } - /* 0, 1, 2, 3 squared */ - if(s->thumb) - return s->foreign ? 9 : 1; - else - return s->foreign ? 4 : 0; -} - -static Prog * -pad(Prog *p, int pc) -{ - Prog *q; - - q = prg(); - q->as = AMOVW; - q->line = p->line; - q->from.type = D_REG; - q->from.reg = REGTMP; - q->to.type = D_REG; - q->to.reg = REGTMP; - q->pc = pc; - q->link = p->link; - return q; -} - -static int scan(Prog *op, Prog *p, int c) { Prog *q; @@ -182,7 +102,6 @@ span(void) otxt = c; for(cursym = textp; cursym != nil; cursym = cursym->next) { p = cursym->text; - setarch(p); p->pc = c; cursym->value = c; @@ -193,19 +112,14 @@ span(void) if(c-otxt >= 1L<<17) bflag = 1; otxt = c; - if(thumb && blitrl) - pool.extra += brextra(p); for(op = p, p = p->link; p != P; op = p, p = p->link) { curp = p; - setarch(p); p->pc = c; o = oplook(p); m = o->size; // must check literal pool here in case p generates many instructions if(blitrl){ - if(thumb && isbranch(p)) - pool.extra += brextra(p); if(checkpool(op, p->as == ACASE ? casesz(p) : m)) c = p->pc = scan(op, p, c); } @@ -230,8 +144,6 @@ span(void) c += m; } if(blitrl){ - if(thumb && isbranch(op)) - pool.extra += brextra(op); if(checkpool(op, 0)) c = scan(op, P, c); } @@ -253,10 +165,7 @@ span(void) cursym->value = c; for(p = cursym->text; p != P; p = p->link) { curp = p; - setarch(p); p->pc = c; - if(thumb && isbranch(p)) - nocache(p); o = oplook(p); /* very large branches if(o->type == 6 && p->cond) { @@ -298,74 +207,6 @@ span(void) } } - if(seenthumb){ // branch resolution - int passes = 0; - int lastc = 0; - int again; - Prog *oop; - - loop: - passes++; - if(passes > 100){ - diag("span looping !"); - errorexit(); - } - c = INITTEXT; - oop = op = nil; - again = 0; - for(cursym = textp; cursym != nil; cursym = cursym->next) { - cursym->value = c; - for(p = cursym->text; p != P; oop = op, op = p, p = p->link) { - curp = p; - setarch(p); - if(p->pc != c) - again = 1; - p->pc = c; - if(thumb && isbranch(p)) - nocache(p); - o = oplook(p); - m = o->size; - if(passes == 1 && thumb && isbranch(p)){ // start conservative so unneeded alignment is not added - if(p->as == ABL) - m = 4; - else - m = 2; - p->align = 0; - } - if(p->align){ - if((p->align == 4 && (c&3)) || (p->align == 2 && !(c&3))){ - if(ispad(op)){ - oop->link = p; - op = oop; - c -= 2; - p->pc = c; - } - else{ - op->link = pad(op, c); - op = op->link; - c += 2; - p->pc = c; - } - again = 1; - } - } - if(m == 0) { - if(p->as == ATEXT) { - autosize = p->to.offset + 4; - if(p->from.sym != S) - p->from.sym->value = c; - continue; - } - } - c += m; - } - cursym->size = c - cursym->value; - } - if(c != lastc || again){ - lastc = c; - goto loop; - } - } c = rnd(c, 8); /* @@ -378,7 +219,6 @@ span(void) */ for(cursym = textp; cursym != nil; cursym = cursym->next) { p = cursym->text; - setarch(p); autosize = p->to.offset + 4; symgrow(cursym, cursym->size); @@ -412,13 +252,6 @@ span(void) int checkpool(Prog *p, int sz) { - if(thumb){ - if(pool.size >= 0x3fc || (p->pc+sz+pool.extra+2+2)+(pool.size-4)-pool.start-4 >= 0x3fc) - return flushpool(p, 1, 0); - else if(p->link == P) - return flushpool(p, 2, 0); - return 0; - } if(pool.size >= 0xffc || immaddr((p->pc+sz+4)+4+pool.size - pool.start+8) == 0) return flushpool(p, 1, 0); else if(p->link == P) @@ -441,7 +274,7 @@ flushpool(Prog *p, int skip, int force) q->link = blitrl; blitrl = q; } - else if(!force && (p->pc+pool.size-pool.start < (thumb ? 0x3fc+4-pool.extra : 2048))) + else if(!force && (p->pc+pool.size-pool.start < 2048)) return 0; elitrl->link = p->link; p->link = blitrl; @@ -461,10 +294,7 @@ addpool(Prog *p, Adr *a) Prog *q, t; int c; - if(thumb) - c = thumbaclass(a, p); - else - c = aclass(a); + c = aclass(a); t = zprg; t.as = AWORD; @@ -480,12 +310,10 @@ addpool(Prog *p, Adr *a) case C_FOREG: case C_SOREG: case C_HOREG: - case C_GOREG: case C_FAUTO: case C_SAUTO: case C_LAUTO: case C_LACON: - case C_GACON: t.to.type = D_CONST; t.to.offset = instoffset; break; @@ -591,16 +419,6 @@ symaddr(Sym *s) return 0; case STEXT: -/* TODO(rsc): what is this for? -#ifdef CALLEEBX - v += fnpinc(s); -#else - if(s->thumb) - v++; // T bit -#endif -*/ - break; - case SELFDATA: case SRODATA: case SDATA: @@ -768,35 +586,19 @@ oplook(Prog *p) int a1, a2, a3, r; char *c1, *c3; Optab *o, *e; - Optab *otab; - Oprang *orange; - if(thumb){ - otab = thumboptab; - orange = thumboprange; - } - else{ - otab = optab; - orange = oprange; - } a1 = p->optab; if(a1) - return otab+(a1-1); + return optab+(a1-1); a1 = p->from.class; if(a1 == 0) { - if(thumb) - a1 = thumbaclass(&p->from, p) + 1; - else - a1 = aclass(&p->from) + 1; + a1 = aclass(&p->from) + 1; p->from.class = a1; } a1--; a3 = p->to.class; if(a3 == 0) { - if(thumb) - a3 = thumbaclass(&p->to, p) + 1; - else - a3 = aclass(&p->to) + 1; + a3 = aclass(&p->to) + 1; p->to.class = a3; } a3--; @@ -804,35 +606,35 @@ oplook(Prog *p) if(p->reg != NREG) a2 = C_REG; r = p->as; - o = orange[r].start; + o = oprange[r].start; if(o == 0) { a1 = opcross[repop[r]][a1][a2][a3]; if(a1) { p->optab = a1+1; - return otab+a1; + return optab+a1; } - o = orange[r].stop; /* just generate an error */ + o = oprange[r].stop; /* just generate an error */ } if(debug['O']) { print("oplook %A %O %O %O\n", (int)p->as, a1, a2, a3); print(" %d %d\n", p->from.type, p->to.type); } - e = orange[r].stop; + 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-otab)+1; + p->optab = (o-optab)+1; return o; } diag("illegal combination %A %O %O %O, %d %d", p->as, a1, a2, a3, p->from.type, p->to.type); prasm(p); if(o == 0) - o = otab; + o = optab; return o; } @@ -883,9 +685,6 @@ cmp(int a, int b) if(b == C_SBRA) return 1; break; - case C_GBRA: - if(b == C_SBRA || b == C_LBRA) - return 1; case C_HREG: return cmp(C_SP, b) || cmp(C_PC, b); @@ -905,9 +704,6 @@ ocmp(const void *a1, const void *a2) n = p1->as - p2->as; if(n) return n; - n = (p2->flag&V4) - (p1->flag&V4); /* architecture version */ - if(n) - return n; n = p1->a1 - p2->a1; if(n) return n; @@ -925,15 +721,11 @@ buildop(void) { int i, n, r; - armv4 = 1; 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 & V4) && !armv4) { - optab[n].as = AXXX; - break; - } + ; qsort(optab, n, sizeof(optab[0]), ocmp); for(i=0; i<n; i++) { r = optab[i].as; @@ -1023,6 +815,8 @@ buildop(void) 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]; break; diff --git a/src/cmd/5l/thumb.c b/src/cmd/5l/thumb.c deleted file mode 100644 index a6f729bed..000000000 --- a/src/cmd/5l/thumb.c +++ /dev/null @@ -1,1658 +0,0 @@ -// Inferno utils/5l/thumb.c -// http://code.google.com/p/inferno-os/source/browse/utils/5l/thumb.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. - -#include "l.h" -#include "../ld/lib.h" - -static int32 thumboprr(int); -static int32 thumboprrr(int, int); -static int32 thumbopirr(int , int); -static int32 thumbopri(int); -static int32 thumbophh(int); -static int32 thumbopbra(int); -static int32 thumbopmv(int, int); -static void lowreg(Prog *, int); -static void mult(Prog *, int, int); -static void numr(Prog *, int, int, int); -static void regis(Prog *, int, int, int); -static void dis(int, int); - -// build a constant using neg, add and shift - only worth it if < 6 bytes */ -static int -immbuildcon(int c, Prog *p) -{ - int n = 0; - - USED(p); - if(c >= 0 && c <= 255) - return 0; // mv - if(c >= -255 && c < 0) // mv, neg - return 1; - if(c >= 256 && c <= 510) // mv, add - return 1; - if(c < 0) - return 0; - while(!(c & 1)){ - n++; - c >>= 1; - } - if(c >= 0 && c <= 255) // mv, lsl - return 1; - return 0; -} - -// positive 5 bit offset from register - O(R) -// positive 8 bit offset from register - mov O, R then [R, R] -// otherwise O goes in literal pool - mov O1(PC), R then [R, R] -static int -immoreg(int off, Prog *p) -{ - int v = 1; - int as = p->as; - - if(off < 0) - return C_GOREG; - if(as == AMOVW) - v = 4; - else if(as == AMOVH || as == AMOVHU) - v = 2; - else if(as == AMOVB || as == AMOVBU) - v = 1; - else - diag("bad op in immoreg"); - if(off/v <= 31) - return C_SOREG; - if(off <= 255) - return C_LOREG; - return C_GOREG; -} - -// positive 8 bit - mov O, R then 0(R) -// otherwise O goes in literal pool - mov O1(PC), R then 0(R) -static int -immacon(int off, Prog *p, int t1, int t2) -{ - USED(p); - if(off < 0) - return t2; - if(off <= 255) - return t1; - return t2; -} - -// unsigned 8 bit in words -static int -immauto(int off, Prog *p) -{ - if(p->as != AMOVW) - diag("bad op in immauto"); - mult(p, off, 4); - if(off >= 0 && off <= 1020) - return C_SAUTO; - return C_LAUTO; -} - -static int -immsmall(int off, Prog *p, int t1, int t2, int t3) -{ - USED(p); - if(off >= 0 && off <= 7) - return t1; - if(off >= 0 && off <= 255) - return t2; - return t3; -} - -static int -immcon(int off, Prog *p) -{ - int as = p->as; - - if(as == ASLL || as == ASRL || as == ASRA) - return C_SCON; - if(p->to.type == D_REG && p->to.reg == REGSP){ - if(as == AADD || as == ASUB){ - if(off >= 0 && off <= 508) - return C_SCON; - if(as == ASUB){ - p->as = AADD; - p->from.offset = -p->from.offset; - } - return C_LCON; - } - diag("unknown type in immcon"); - } - if(as == AADD || as == ASUB){ - if(p->reg != NREG) - return immsmall(off, p, C_SCON, C_LCON, C_GCON); - return immacon(off, p, C_SCON, C_LCON); - } - if(as == AMOVW && p->from.type == D_CONST && p->to.type == D_REG && immbuildcon(off, p)) - return C_BCON; - if(as == ACMP && p->from.type == D_CONST && immbuildcon(off, p)) - return C_BCON; - if(as == ACMP || as == AMOVW) - return immacon(off, p, C_SCON, C_LCON); - return C_LCON; -} - -int -thumbaclass(Adr *a, Prog *p) -{ - Sym *s; - int t; - - switch(a->type) { - case D_NONE: - return C_NONE; - case D_REG: - if(a->reg == REGSP) - return C_SP; - if(a->reg == REGPC) - return C_PC; - if(a->reg >= 8) - return C_HREG; - return C_REG; - case D_SHIFT: - diag("D_SHIFT in thumbaclass"); - return C_SHIFT; - case D_FREG: - diag("D_FREG in thumbaclass"); - return C_FREG; - case D_FPCR: - diag("D_FPCR in thumbaclass"); - 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; - } - t = a->sym->type; - if(t == 0 || t == SXREF) { - diag("undefined external: %s in %s\n", - a->sym->name, TNAME); - a->sym->type = SDATA; - } - instoffset = a->sym->value + a->offset; - return C_ADDR; /* INITDAT unknown at this stage */ - case D_AUTO: - instoffset = autosize + a->offset; - return immauto(instoffset, p); - case D_PARAM: - instoffset = autosize + a->offset + 4L; -// print("D_PARAM %s %d+%d+%d = %d\n", a->sym != S ? a->sym->name : "noname", autosize, a->offset, 4, autosize+a->offset+4); - return immauto(instoffset, p); - case D_NONE: - instoffset = a->offset; - if(a->reg == REGSP) - return immauto(instoffset, p); - else - return immoreg(instoffset, p); - } - return C_GOK; - case D_PSR: - diag("D_PSR in thumbaclass"); - return C_PSR; - case D_OCONST: - switch(a->name) { - case D_EXTERN: - case D_STATIC: - s = a->sym; - t = s->type; - if(t == 0 || t == SXREF) { - diag("undefined external: %s in %s\n", - s->name, TNAME); - s->type = SDATA; - } - instoffset = s->value + a->offset; - if(s->type == STEXT){ - instoffset = s->value + a->offset; -#ifdef CALLEEBX - instoffset += fnpinc(s); -#else - if(s->thumb) - instoffset++; // T bit -#endif - return C_LCON; - } - return C_LCON; /* INITDAT unknown at this stage */ - // return immcon(instoffset, p); - } - return C_GOK; - case D_FCONST: - diag("D_FCONST in thumaclass"); - return C_LFCON; - case D_CONST: - switch(a->name) { - case D_NONE: - instoffset = a->offset; - if(a->reg != NREG) - goto aconsize; - return immcon(instoffset, p); - case D_EXTERN: - case D_STATIC: - s = a->sym; - if(s == S) - break; - t = s->type; - switch(t) { - case 0: - case SXREF: - diag("undefined external: %s in %s\n", - s->name, TNAME); - s->type = SDATA; - break; - case SCONST: - case STEXT: - instoffset = s->value + a->offset; -#ifdef CALLEEBX - instoffset += fnpinc(s); -#else - if(s->thumb) - instoffset++; // T bit -#endif - return C_LCON; - } - instoffset = s->value + a->offset; - return C_LCON; /* INITDAT unknown at this stage */ - // return immcon(instoffset, p); - case D_AUTO: - instoffset = autosize + a->offset; - goto aconsize; - case D_PARAM: - instoffset = autosize + a->offset + 4L; - aconsize: - if(p->from.reg == REGSP || p->from.reg == NREG) - return instoffset >= 0 && instoffset < 1024 ? C_SACON : C_GACON; - else if(p->from.reg == p->to.reg) - return immacon(instoffset, p, C_SACON, C_GACON); - return immsmall(instoffset, p, C_SACON, C_LACON, C_GACON); - } - return C_GOK; - case D_BRANCH: { - int v, va; - - p->align = 0; - v = -4; - va = 0; - if(p->cond != P){ - v = (p->cond->pc - p->pc) - 4; - va = p->cond->pc; - } - instoffset = v; - if(p->as == AB){ - if(v >= -2048 && v <= 2046) - return C_SBRA; - p->align = 4; - instoffset = va; - return C_LBRA; - } - if(p->as == ABL){ -#ifdef CALLEEBX - int e; - - if((e = fninc(p->to.sym))) { - v += e; - va += e; - instoffset += e; - } -#endif - if(v >= -4194304 && v <= 4194302) - return C_SBRA; - p->align = 2; - instoffset = va; - return C_LBRA; - } - if(p->as == ABX){ - v = va; - if(v >= 0 && v <= 255) - return C_SBRA; - p->align = 2; - instoffset = va; - return C_LBRA; - } - if(v >= -256 && v <= 254) - return C_SBRA; - if(v >= -(2048-2) && v <= (2046+2)) - return C_LBRA; - p->align = 2; - instoffset = va; - return C_GBRA; - } - } - return C_GOK; -} - -// as a1 a2 a3 type size param lit vers -Optab thumboptab[] = -{ - { ATEXT, C_ADDR, C_NONE, C_LCON, 0, 0, 0 }, - { ATEXT, C_ADDR, C_REG, C_LCON, 0, 0, 0 }, - { AMVN, C_REG, C_NONE, C_REG, 1, 2, 0 }, - { ASRL, C_REG, C_NONE, C_REG, 1, 2, 0 }, - { ACMP, C_REG, C_REG, C_NONE, 1, 2, 0 }, - { ACMN, C_REG, C_REG, C_NONE, 1, 2, 0 }, - { AADD, C_REG, C_REG, C_REG, 2, 2, 0 }, - { AADD, C_REG, C_NONE, C_REG, 2, 2, 0 }, - { AADD, C_SCON, C_REG, C_REG, 3, 2, 0 }, - { AADD, C_LCON, C_REG, C_REG, 49, 4, 0 }, - { AADD, C_GCON, C_REG, C_REG, 36, 4, 0, LFROM }, - // { AADD, C_LCON, C_NONE, C_REG, 3, 2, 0, LFROM }, - { ASRL, C_SCON, C_REG, C_REG, 4, 2, 0 }, - { ASRL, C_SCON, C_NONE, C_REG, 4, 2, 0 }, - { AADD, C_SCON, C_NONE, C_REG, 5, 2, 0 }, - { AADD, C_LCON, C_NONE, C_REG, 37, 4, 0, LFROM }, - { ACMP, C_SCON, C_REG, C_NONE, 5, 2, 0 }, - { ACMP, C_BCON, C_REG, C_NONE, 48, 6, 0 }, - { ACMP, C_LCON, C_REG, C_NONE, 39, 4, 0, LFROM }, - { AMOVW, C_SCON, C_NONE, C_REG, 5, 2, 0 }, - { AMOVW, C_BCON, C_NONE, C_REG, 47, 4, 0 }, - { AMOVW, C_LCON, C_NONE, C_REG, 38, 2, 0, LFROM }, - // { AADD, C_LCON, C_PC, C_REG, 6, 2, 0, LFROM }, - // { AADD, C_LCON, C_SP, C_REG, 6, 2, 0, LFROM }, - { AADD, C_SCON, C_NONE, C_SP, 7, 2, 0 }, - { AADD, C_LCON, C_NONE, C_SP, 40, 4, 0, LFROM }, - { AADD, C_REG, C_NONE, C_HREG, 8, 2, 0 }, - { AADD, C_HREG, C_NONE, C_REG, 8, 2, 0 }, - { AADD, C_HREG, C_NONE, C_HREG, 8, 2, 0 }, - { AMOVW, C_REG, C_NONE, C_HREG, 8, 2, 0 }, - { AMOVW, C_HREG, C_NONE, C_REG, 8, 2, 0 }, - { AMOVW, C_HREG, C_NONE, C_HREG, 8, 2, 0 }, - { ACMP, C_REG, C_HREG, C_NONE, 8, 2, 0 }, - { ACMP, C_HREG, C_REG, C_NONE, 8, 2, 0 }, - { ACMP, C_HREG, C_HREG, C_NONE, 8, 2, 0 }, - { AB, C_NONE, C_NONE, C_SBRA, 9, 2, 0, LPOOL }, - { ABEQ, C_NONE, C_NONE, C_SBRA, 10, 2, 0 }, - { ABL, C_NONE, C_NONE, C_SBRA, 11, 4, 0 }, - { ABX, C_NONE, C_NONE, C_SBRA, 12, 10, 0 }, - { AB, C_NONE, C_NONE, C_LBRA, 41, 8, 0, LPOOL }, - { ABEQ, C_NONE, C_NONE, C_LBRA, 46, 4, 0 }, - { ABL, C_NONE, C_NONE, C_LBRA, 43, 14, 0 }, - { ABX, C_NONE, C_NONE, C_LBRA, 44, 14, 0 }, - { ABEQ, C_NONE, C_NONE, C_GBRA, 42, 10, 0 }, - // { AB, C_NONE, C_NONE, C_SOREG, 13, 0, 0 }, - // { ABL, C_NONE, C_NONE, C_SOREG, 14, 0, 0 }, - { ABL, C_NONE, C_NONE, C_REG, 51, 4, 0 }, - { ABX, C_NONE, C_NONE, C_REG, 15, 8, 0 }, - { ABX, C_NONE, C_NONE, C_HREG, 15, 8, 0 }, - { ABXRET, C_NONE, C_NONE, C_REG, 45, 2, 0 }, - { ABXRET, C_NONE, C_NONE, C_HREG, 45, 2, 0 }, - { ASWI, C_NONE, C_NONE, C_LCON, 16, 2, 0 }, - { AWORD, C_NONE, C_NONE, C_LCON, 17, 4, 0 }, - { AWORD, C_NONE, C_NONE, C_GCON, 17, 4, 0 }, - { AWORD, C_NONE, C_NONE, C_ADDR, 17, 4, 0 }, - { ADWORD, C_LCON, C_NONE, C_LCON, 50, 8, 0 }, - { AMOVW, C_SAUTO, C_NONE, C_REG, 18, 2, REGSP }, - { AMOVW, C_LAUTO, C_NONE, C_REG, 33, 6, 0, LFROM }, - // { AMOVW, C_OFFPC, C_NONE, C_REG, 18, 2, REGPC, LFROM }, - { AMOVW, C_SOREG, C_NONE, C_REG, 19, 2, 0 }, - { AMOVHU, C_SOREG, C_NONE, C_REG, 19, 2, 0 }, - { AMOVBU, C_SOREG, C_NONE, C_REG, 19, 2, 0 }, - { AMOVW, C_REG, C_NONE, C_SAUTO, 20, 2, 0 }, - { AMOVW, C_REG, C_NONE, C_LAUTO, 34, 6, 0, LTO }, - { AMOVW, C_REG, C_NONE, C_SOREG, 21, 2, 0 }, - { AMOVH, C_REG, C_NONE, C_SOREG, 21, 2, 0 }, - { AMOVB, C_REG, C_NONE, C_SOREG, 21, 2, 0 }, - { AMOVHU, C_REG, C_NONE, C_SOREG, 21, 2, 0 }, - { AMOVBU, C_REG, C_NONE, C_SOREG, 21, 2, 0 }, - { AMOVW, C_REG, C_NONE, C_REG, 22, 2, 0 }, - { AMOVB, C_REG, C_NONE, C_REG, 23, 4, 0 }, - { AMOVH, C_REG, C_NONE, C_REG, 23, 4, 0 }, - { AMOVBU, C_REG, C_NONE, C_REG, 23, 4, 0 }, - { AMOVHU, C_REG, C_NONE, C_REG, 23, 4, 0 }, - { AMOVH, C_SOREG, C_NONE, C_REG, 24, 4, 0 }, - { AMOVB, C_SOREG, C_NONE, C_REG, 24, 4, 0 }, - { AMOVW, C_SACON, C_NONE, C_REG, 25, 2, 0 }, - { AMOVW, C_LACON, C_NONE, C_REG, 35, 4, 0 }, - { AMOVW, C_GACON, C_NONE, C_REG, 35, 4, 0, LFROM }, - { AMOVM, C_LCON, C_NONE, C_REG, 26, 2, 0 }, - { AMOVM, C_REG, C_NONE, C_LCON, 27, 2, 0 }, - { AMOVW, C_LOREG, C_NONE, C_REG, 28, 4, 0 }, - { AMOVH, C_LOREG, C_NONE, C_REG, 28, 4, 0 }, - { AMOVB, C_LOREG, C_NONE, C_REG, 28, 4, 0 }, - { AMOVHU, C_LOREG, C_NONE, C_REG, 28, 4, 0 }, - { AMOVBU, C_LOREG, C_NONE, C_REG, 28, 4, 0 }, - { AMOVW, C_REG, C_NONE, C_LOREG, 29, 4, 0 }, - { AMOVH, C_REG, C_NONE, C_LOREG, 29, 4, 0 }, - { AMOVB, C_REG, C_NONE, C_LOREG, 29, 4, 0 }, - { AMOVHU, C_REG, C_NONE, C_LOREG, 29, 4, 0 }, - { AMOVBU, C_REG, C_NONE, C_LOREG, 29, 4, 0 }, - { AMOVW, C_GOREG, C_NONE, C_REG, 28, 4, 0, LFROM }, - { AMOVH, C_GOREG, C_NONE, C_REG, 28, 4, 0, LFROM }, - { AMOVB, C_GOREG, C_NONE, C_REG, 28, 4, 0, LFROM }, - { AMOVHU, C_GOREG, C_NONE, C_REG, 28, 4, 0, LFROM }, - { AMOVBU, C_GOREG, C_NONE, C_REG, 28, 4, 0, LFROM }, - { AMOVW, C_REG, C_NONE, C_GOREG, 29, 4, 0, LTO }, - { AMOVH, C_REG, C_NONE, C_GOREG, 29, 4, 0, LTO }, - { AMOVB, C_REG, C_NONE, C_GOREG, 29, 4, 0, LTO }, - { AMOVHU, C_REG, C_NONE, C_GOREG, 29, 4, 0, LTO }, - { AMOVBU, C_REG, C_NONE, C_GOREG, 29, 4, 0, LTO }, - { AMOVW, C_ADDR, C_NONE, C_REG, 30, 4, 0, LFROM }, - { AMOVH, C_ADDR, C_NONE, C_REG, 32, 6, 0, LFROM }, - { AMOVB, C_ADDR, C_NONE, C_REG, 32, 6, 0, LFROM }, - { AMOVHU, C_ADDR, C_NONE, C_REG, 30, 4, 0, LFROM }, - { AMOVBU, C_ADDR, C_NONE, C_REG, 30, 4, 0, LFROM }, - { AMOVW, C_REG, C_NONE, C_ADDR, 31, 4, 0, LTO }, - { AMOVH, C_REG, C_NONE, C_ADDR, 31, 4, 0, LTO }, - { AMOVB, C_REG, C_NONE, C_ADDR, 31, 4, 0, LTO }, - { AMOVHU, C_REG, C_NONE, C_ADDR, 31, 4, 0, LTO }, - { AMOVBU, C_REG, C_NONE, C_ADDR, 31, 4, 0, LTO }, - - { AXXX, C_NONE, C_NONE, C_NONE, 0, 2, 0 }, -}; - -#define OPCNTSZ 52 -int opcount[OPCNTSZ]; - -// is this too pessimistic ? -int -brextra(Prog *p) -{ - int c; - - // +2 is for padding - if(p->as == ATEXT) - return 0-0+2; - if(!isbranch(p)) - diag("bad op in brextra()"); - c = thumbaclass(&p->to, p); - switch(p->as){ - case AB: - if(c != C_SBRA) - return 0; - return 8-2+2; - case ABL: - if(c != C_SBRA) - return 0; - return 14-4+2; - case ABX: - if(c == C_REG || c == C_HREG) - return 0; -#ifdef CALLEEBX - diag("ABX $I in brextra"); -#endif - if(c != C_SBRA) - return 0; - return 14-10+2; - default: - if(c == C_GBRA) - return 0; - if(c == C_LBRA) - return 10-4+2; - return 10-2+2; - } - return 0; -} - -#define high(r) ((r)>=8) - -static int32 -mv(Prog *p, int r, int off) -{ - int v, o; - if(p != nil && p->cond != nil){ // in literal pool - v = p->cond->pc - p->pc - 4; - if(p->cond->pc & 3) - diag("mv: bad literal pool alignment"); - if(v & 3) - v += 2; // ensure M(4) offset - mult(p, v, 4); - off = v/4; - numr(p, off, 0, 255); - o = 0x9<<11; - } - else{ - numr(p, off, 0, 255); - o = 0x4<<11; - } - o |= (r<<8) | off; - return o; -} - -static void -mvcon(Prog *p, int r, int c, int32 *o1, int32 *o2) -{ - int op = 0, n = 0; - - if(c >= 0 && c <= 255) - diag("bad c in mvcon"); - if(c >= -255 && c < 0) // mv, neg - c = -c; - else if(c >= 256 && c <= 510){ // mv, add - n = rand()%(511-c) + (c-255); - c -= n; - // n = c-255; - // c = 255; - op = AADD; - } - else{ - if(c < 0) - diag("-ve in mvcon"); - while(!(c & 1)){ - n++; - c >>= 1; - } - if(c >= 0 && c <= 255) // mv, lsl - op = ASLL; - else - diag("bad shift in mvcon"); - } - *o1 = mv(p, r, c); - switch(op){ - case 0: - *o2 = (1<<14) | (9<<6) | (r<<3) | r; - break; - case AADD: - *o2 = (6<<11) | (r<<8) | n; - break; - case ASLL: - *o2 = (n<<6) | (r<<3) | r; - break; - } -} - -static int32 -mvlh(int rs, int rd) -{ - int o = 0x46<<8; - - if(high(rs)){ - rs -= 8; - o |= 1<<6; - } - if(high(rd)){ - rd -= 8; - o |= 1<<7; - } - o |= (rs<<3) | rd; - return o; -} - -void -thumbbuildop() -{ - int i, n, r; - Optab *optab = thumboptab; - Oprang *oprange = thumboprange; - - for(n=0; optab[n].as != AXXX; n++) - ; - 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: - 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 AMVN: - oprange[AADC] = oprange[r]; - oprange[ASBC] = oprange[r]; - oprange[AMUL] = oprange[r]; - oprange[AAND] = oprange[r]; - oprange[AEOR] = oprange[r]; - oprange[AORR] = oprange[r]; - oprange[ABIC] = oprange[r]; - oprange[AMULU] = oprange[r]; - break; - case ACMN: - oprange[ATST] = oprange[r]; - break; - case ASRL: - oprange[ASRA] = oprange[r]; - oprange[ASLL] = oprange[r]; - break; - case AADD: - oprange[ASUB] = oprange[r]; - break; - } - } -} - -void -thumbasmout(Prog *p, Optab *o) -{ - int32 o1, o2, o3, o4, o5, o6, o7, v; - int r, rf, rt; - - rf = p->from.reg; - rt = p->to.reg; - r = p->reg; - o1 = o2 = o3 = o4 = o5 = o6 = o7 = 0; -if(debug['P']) print("%ux: %P type %d %d\n", (uint32)(p->pc), p, o->type, p->align); - opcount[o->type] += o->size; - switch(o->type) { - default: - diag("unknown asm %d", o->type); - prasm(p); - break; - case 0: /* pseudo ops */ -if(debug['G']) print("%ux: %s: thumb\n", (uint32)(p->pc), p->from.sym->name); - break; - case 1: /* op R, -, R or op R, R, - */ - o1 = thumboprr(p->as); - if(rt == NREG) - rt = r; - lowreg(p, rf); - lowreg(p, rt); - o1 |= (0x10<<10) | (rf<<3) | rt; - break; - case 2: /* add/sub R, R, R or add/sub R, -, R */ - o1 = p->as == AADD ? 0x0<<9 : 0x1<<9; - if(r == NREG) - r = rt; - lowreg(p, rf); - lowreg(p, r); - lowreg(p, rt); - o1 |= (0x6<<10) | (rf<<6) | (r<<3) | rt; - break; - case 3: /* add/sub $I, R, R or add/sub $I, -, R */ - thumbaclass(&p->from, p); - o1 = p->as == AADD ? 0x0<<9 : 0x1<<9; - if(r == NREG) - r = rt; - numr(p, instoffset, 0, 7); - lowreg(p, r); - lowreg(p, rt); - o1 |= (0x7<<10) | (instoffset<<6) | (r<<3) | rt; - break; - case 4: /* shift $I, R, R or shift $I, -, R */ - thumbaclass(&p->from, p); - if(instoffset < 0) - diag("negative shift in thumbasmout"); - instoffset %= 32; - o1 = thumbopri(p->as); - if(r == NREG) - r = rt; - numr(p, instoffset, 0, 31); - lowreg(p, r); - lowreg(p, rt); - o1 |= (0x0<<13) | (instoffset<<6) | (r<<3) | rt; - break; - case 5: /* add/sub/mov $I, -, R or cmp $I, R, - */ - thumbaclass(&p->from, p); - o1 = thumbopri(p->as); - if(rt == NREG) - rt = r; - numr(p, instoffset, 0, 255); - lowreg(p, rt); - o1 |= (0x1<<13) | (rt<<8) | instoffset; - break; - case 6: /* add $I, PC/SP, R */ - if(p->as == ASUB) - diag("subtract in add $I, PC/SP, R"); - thumbaclass(&p->from, p); - o1 = r == REGSP ? 0x1<<11 : 0x0<<11; - numr(p, instoffset, 0, 255); - regis(p, r, REGSP, REGPC); - lowreg(p, rt); - o1 |= (0xa<<12) | (rt<<8) | instoffset; - break; - case 7: /* add, sub $I, SP */ - thumbaclass(&p->from, p); - o1 = p->as == AADD ? 0x0<<7 : 0x1<<7; - numr(p, instoffset, 0, 508); - mult(p, instoffset, 4); - regis(p, rt, REGSP, REGSP); - o1 |= (0xb0<<8) | (instoffset>>2); - break; - case 8: /* add/mov/cmp R, R where at least 1 reg is high */ - o1 = 0; - if(rt == NREG) - rt = r; - if(high(rf)){ - o1 |= 1<<6; - rf -= 8; - } - if(high(rt)){ - o1 |= 2<<6; - rt -= 8; - } - if(o1 == 0) - diag("no high register(%P)", p); - o1 |= thumbophh(p->as); - o1 |= (0x11<<10) | (rf<<3) | rt; - break; - case 9: /* B $I */ - thumbaclass(&p->to, p); - numr(p, instoffset, -2048, 2046); - o1 = (0x1c<<11) | ((instoffset>>1)&0x7ff); - break; - case 10: /* Bcc $I */ - thumbaclass(&p->to, p); - numr(p, instoffset, -256, 254); - o1 = thumbopbra(p->as); - o1 |= (0xd<<12) | ((instoffset>>1)&0xff); - break; - case 11: /* BL $I */ - thumbaclass(&p->to, p); - numr(p, instoffset, -4194304, 4194302); - o1 = (0x1e<<11) | ((instoffset>>12)&0x7ff); - o2 = (0x1f<<11) | ((instoffset>>1)&0x7ff); - break; - case 12: /* BX $I */ -#ifdef CALLEEBX - diag("BX $I case"); -#endif - thumbaclass(&p->to, p); - if(p->to.sym->thumb) - instoffset |= 1; // T bit - o1 = mvlh(REGPC, REGTMPT); - o2 = (0x6<<11) | (REGTMPT<<8) | 7; // add 7, RTMP (T bit + PC offset) - o3 = mvlh(REGTMPT, REGLINK); - o4 = mv(nil, REGTMPT, instoffset); - o5 = (0x11c<<6) | (REGTMPT<<3); - // o1 = mv(nil, REGTMPT, v); - // o2 = (0x11b<<6) | (REGPC<<3) | REGLINK; - // o3 = (0x11c<<6) | (REGTMPT<<3); - break; - case 13: /* B O(R) */ - diag("B O(R)"); - break; - case 14: /* BL O(R) */ - diag("BL O(R)"); - break; - case 15: /* BX R */ - o1 = mvlh(REGPC, REGTMPT); - o2 = (0x6<<11) | (REGTMPT<<8) | 5; // add 5, RTMP (T bit + PC offset) - o3 = mvlh(REGTMPT, REGLINK); - o4 = 0; - if(high(rt)){ - rt -= 8; - o4 |= 1<<6; - } - o4 |= (0x8e<<7) | (rt<<3); - // o1 = (0x11c<<6) | (rt<<3); - break; - case 16: /* SWI $I */ - thumbaclass(&p->to, p); - numr(p, instoffset, 0, 255); - o1 = (0xdf<<8) | instoffset; - break; - case 17: /* AWORD */ - thumbaclass(&p->to, p); - o1 = instoffset&0xffff; - o2 = (instoffset>>16)&0xffff; - break; - case 18: /* AMOVW O(SP), R and AMOVW O(PC), R */ - thumbaclass(&p->from, p); - rf = o->param; - o1 = rf == REGSP ? 0x13<<11 : 0x9<<11; - regis(p, rf, REGSP, REGPC); - lowreg(p, rt); - mult(p, instoffset, 4); - numr(p, instoffset/4, 0, 255); - o1 |= (rt<<8) | (instoffset/4); - break; - case 19: /* AMOVW... O(R), R */ - thumbaclass(&p->from, p); - o1 = thumbopmv(p->as, 1); - v = 4; - if(p->as == AMOVHU) - v = 2; - else if(p->as == AMOVBU) - v = 1; - mult(p, instoffset, v); - lowreg(p, rf); - lowreg(p, rt); - numr(p, instoffset/v, 0, 31); - o1 |= ((instoffset/v)<<6) | (rf<<3) | rt; - break; - case 20: /* AMOVW R, O(SP) */ - thumbaclass(&p->to, p); - o1 = 0x12<<11; - if(rt != NREG) regis(p, rt, REGSP, REGSP); - lowreg(p, rf); - mult(p, instoffset, 4); - numr(p, instoffset/4, 0, 255); - o1 |= (rf<<8) | (instoffset/4); - break; - case 21: /* AMOVW... R, O(R) */ - thumbaclass(&p->to, p); - o1 = thumbopmv(p->as, 0); - v = 4; - if(p->as == AMOVHU || p->as == AMOVH) - v = 2; - else if(p->as == AMOVBU || p->as == AMOVB) - v = 1; - lowreg(p, rf); - lowreg(p, rt); - mult(p, instoffset, v); - numr(p, instoffset/v, 0, 31); - o1 |= ((instoffset/v)<<6) | (rt<<3) | rf; - break; - case 22: /* AMOVW R, R -> ASLL $0, R, R */ - o1 = thumbopri(ASLL); - lowreg(p, rf); - lowreg(p, rt); - o1 |= (0x0<<13) | (rf<<3) | rt; - break; - case 23: /* AMOVB/AMOVH/AMOVBU/AMOVHU R, R */ - o1 = thumbopri(ASLL); - o2 = p->as == AMOVB || p->as == AMOVH ? thumbopri(ASRA) : thumbopri(ASRL); - v = p->as == AMOVB || p->as == AMOVBU ? 24 : 16; - lowreg(p, rf); - lowreg(p, rt); - o1 |= (0x0<<13) | (v<<6) | (rf<<3) | rt; - o2 |= (0x0<<13) | (v<<6) | (rt<<3) | rt; - break; - case 24: /* AMOVH/AMOVB O(R), R -> AMOVH/AMOVB [R, R], R */ - thumbaclass(&p->from, p); - lowreg(p, rf); - lowreg(p, rt); - if(rf == rt) - r = REGTMPT; - else - r = rt; - if(p->as == AMOVB) - numr(p, instoffset, 0, 31); - else{ - mult(p, instoffset, 2); - numr(p, instoffset, 0, 62); - } - o1 = mv(p, r, instoffset); - o2 = p->as == AMOVH ? 0x2f<<9 : 0x2b<<9; - o2 |= (r<<6) | (rf<<3) | rt; - break; - case 25: /* MOVW $sacon, R */ - thumbaclass(&p->from, p); -// print("25: %d %d %d %d\n", instoffset, rf, r, rt); - if(rf == NREG) - rf = REGSP; - lowreg(p, rt); - if(rf == REGSP){ - mult(p, instoffset, 4); - numr(p, instoffset>>2, 0, 255); - o1 = (0x15<<11) | (rt<<8) | (instoffset>>2); // add $O, SP, R - } - else if(rf == rt){ - numr(p, instoffset, 0, 255); - o1 = (0x6<<11) | (rt<<8) | instoffset; // add $O, R - } - else{ - lowreg(p, rf); - numr(p, instoffset, 0, 7); - o1 = (0xe<<9) | (instoffset<<6) | (rf<<3) | rt; // add $O, Rs, Rd - } - break; - case 26: /* AMOVM $c, oreg -> stmia */ - lowreg(p, rt); - numr(p, p->from.offset, -256, 255); - o1 = (0x18<<11) | (rt<<8) | (p->from.offset&0xff); - break; - case 27: /* AMOVM oreg, $c ->ldmia */ - lowreg(p, rf); - numr(p, p->to.offset, -256, 256); - o1 = (0x19<<11) | (rf<<8) | (p->to.offset&0xff); - break; - case 28: /* AMOV* O(R), R -> AMOV* [R, R], R (offset large) */ - thumbaclass(&p->from, p); - lowreg(p, rf); - lowreg(p, rt); - if(rf == rt) - r = REGTMPT; - else - r = rt; - o1 = mv(p, r, instoffset); - o2 = thumboprrr(p->as, 1); - o2 |= (r<<6) | (rf<<3) | rt; - break; - case 29: /* AMOV* R, O(R) -> AMOV* R, [R, R] (offset large) */ - thumbaclass(&p->to, p); - lowreg(p, rf); - lowreg(p, rt); - if(rt == REGTMPT){ // used as tmp reg - if(instoffset >= 0 && instoffset <= 255){ - o1 = (1<<13) | (2<<11) | (rt<<8) | instoffset; // add $O, R7 - o2 = thumbopirr(p->as, 0); - o2 |= (0<<6) | (rt<<3) | rf; // mov* R, 0(R) - } - else - diag("big offset - case 29"); - } - else{ - o1 = mv(p, REGTMPT, instoffset); - o2 = thumboprrr(p->as, 0); - o2 |= (REGTMPT<<6) | (rt<<3) | rf; - } - break; - case 30: /* AMOVW... *addr, R */ - diag("likely broken"); // does this still refer to SB? - thumbaclass(&p->from, p); - o1 = mv(p, rt, instoffset); // MOV addr, rtmp - o2 = thumbopmv(p->as, 1); - lowreg(p, rt); - o2 |= (rt<<3) | rt; // MOV* 0(rtmp), R - break; - case 31: /* AMOVW... R, *addr */ - diag("likely broken"); // does this still refer to SB? - thumbaclass(&p->to, p); - o1 = mv(p, REGTMPT, instoffset); - o2 = thumbopmv(p->as, 0); - lowreg(p, rf); - o2 |= (REGTMPT<<3) | rf; - break; - case 32: /* AMOVH/AMOVB *addr, R -> AMOVH/AMOVB [R, R], R */ - thumbaclass(&p->from, p); - o1 = mv(p, rt, instoffset); - lowreg(p, rt); - o2 = mv(nil, REGTMPT, 0); - o3 = p->as == AMOVH ? 0x2f<<9 : 0x2b<<9; - o3 |= (REGTMPT<<6) | (rt<<3) | rt; - break; - case 33: /* AMOVW O(SP), R (O large) */ - thumbaclass(&p->from, p); - lowreg(p, rt); - o1 = mv(p, rt, instoffset); - o2 = (0x111<<6) | (REGSP-8)<<3 | rt; // add SP, rt - o3 = thumbopmv(p->as, 1); - o3 |= (rt<<3) | rt; - break; - case 34: /* AMOVW R, O(SP) (O large) */ - thumbaclass(&p->to, p); - lowreg(p, rf); - o1 = mv(p, REGTMPT, instoffset); - o2 = (0x111<<6) | (REGSP-8)<<3 | REGTMPT; // add SP, REGTMP - o3 = thumbopmv(p->as, 0); - o3 |= (REGTMPT<<3) | rf; - break; - case 35: /* AMOVW $lacon, R */ - thumbaclass(&p->from, p); - lowreg(p, rt); - if(rf == NREG) - rf = REGSP; - if(rf == rt) - rf = r = REGTMPT; - else - r = rt; -// print("35: io=%d rf=%d rt=%d\n", instoffset, rf, rt); - o1 = mv(p, r, instoffset); // mov O, Rd - if(high(rf)) - o2 = (0x44<<8) | (0x1<<6) | ((rf-8)<<3) | rt; // add Rs, Rd - else - o2 = (0x6<<10) | (rf<<6) | (rt<<3) | rt; // add Rs, Rd - break; - case 36: /* AADD/ASUB $i, r, r when $i too big */ - thumbaclass(&p->from, p); - lowreg(p, r); - lowreg(p, rt); - o1 = mv(p, REGTMPT, instoffset); - o2 = p->as == AADD ? 0xc<<9 : 0xd<<9; - o2 |= (REGTMPT<<6) | (r<<3) | rt; - break; - case 37: /* AADD/ASUB $i, r when $i too big */ - thumbaclass(&p->from, p); - lowreg(p, rt); - o1 = mv(p, REGTMPT, instoffset); - o2 = p->as == AADD ? 0xc<<9 : 0xd<<9; - o2 |= (REGTMPT<<6) | (rt<<3) | rt; - break; - case 38: /* AMOVW $i, r when $i too big */ - thumbaclass(&p->from, p); - lowreg(p, rt); - o1 = mv(p, rt, instoffset); - break; - case 39: /* ACMP $i, r when $i too big */ - thumbaclass(&p->from, p); - lowreg(p, r); - o1 = mv(p, REGTMPT, instoffset); - o2 = (0x10a<<6) | (REGTMPT<<3) | r; - break; - case 40: /* add, sub $I, SP when $I large*/ - thumbaclass(&p->from, p); - if(p->as == ASUB) - instoffset = -instoffset; - o1 = mv(p, REGTMPT, instoffset); - o2 = (0x112<<6) | (REGTMPT<<3) | (REGSP-8); - regis(p, rt, REGSP, REGSP); - break; - case 41: /* BL LBRA */ - thumbaclass(&p->to, p); - o1 = (0x9<<11) | (REGTMPT<<8); // mov 0(pc), r7 - o2 = mvlh(REGTMPT, REGPC); // mov r7, pc - o3 = instoffset&0xffff; // $lab - o4 = (instoffset>>16)&0xffff; - break; - case 42: /* Bcc GBRA */ - thumbaclass(&p->to, p); - o1 = (0xd<<12) | thumbopbra(relinv(p->as)) | (6>>1); // bccnot - // ab lbra - o2 = (0x9<<11) | (REGTMPT<<8); // mov 0(pc), r7 - o3 = mvlh(REGTMPT, REGPC); // mov r7, pc - o4 = instoffset&0xffff; // $lab - o5 = (instoffset>>16)&0xffff; - break; - case 43: /* BL LBRA */ - thumbaclass(&p->to, p); - o1 = mvlh(REGPC, REGTMPT); // mov pc, r7 - o2 = (0x6<<11) | (REGTMPT<<8) | 10; // add 10, r7 - o3 = mvlh(REGTMPT, REGLINK); // mov r7, lr - o4 = (0x9<<11) | (REGTMPT<<8); // mov o(pc), r7 - o5 = mvlh(REGTMPT, REGPC); // mov r7, pc - o6 = instoffset&0xffff; // $lab - o7 = (instoffset>>16)&0xffff; - break; - case 44: /* BX LBRA */ -#ifdef CALLEEBX - diag("BX LBRA case"); -#endif - thumbaclass(&p->to, p); - if(p->to.sym->thumb) - instoffset |= 1; // T bit - o1 = mvlh(REGPC, REGTMPT); // mov pc, r7 - o2 = (0x6<<11) | (REGTMPT<<8) | 11; // add 11, r7 - o3 = mvlh(REGTMPT, REGLINK); // mov r7, lr - o4 = (0x9<<11) | (REGTMPT<<8); // mov o(pc), r7 - o5 = (0x11c<<6) | (REGTMPT<<3); // bx r7 - o6 = instoffset&0xffff; // $lab - o7 = (instoffset>>16)&0xffff; - break; - case 45: /* BX R when returning from fn */ - o1 = 0; - if(high(rt)){ - rt -= 8; - o1 |= 1<<6; - } - o1 |= (0x8e<<7) | (rt<<3); - break; - case 46: /* Bcc LBRA */ - thumbaclass(&p->to, p); - o1 = (0xd<<12) | thumbopbra(relinv(p->as)) | (0>>1); // bccnot - // ab lbra - instoffset -= 2; - numr(p, instoffset, -2048, 2046); - o2 = (0x1c<<11) | ((instoffset>>1)&0x7ff); - break; - case 47: /* mov $i, R where $i can be built */ - thumbaclass(&p->from, p); - mvcon(p, rt, instoffset, &o1, &o2); - break; - case 48: /* ACMP $i, r when $i built up */ - thumbaclass(&p->from, p); - lowreg(p, r); - mvcon(p, REGTMPT, instoffset, &o1, &o2); - o3 = (0x10a<<6) | (REGTMPT<<3) | r; - break; - case 49: /* AADD $i, r, r when $i is between 0 and 255 - could merge with case 36 */ - thumbaclass(&p->from, p); - lowreg(p, r); - lowreg(p, rt); - numr(p, instoffset, 0, 255); - o1 = mv(p, REGTMPT, instoffset); - o2 = p->as == AADD ? 0xc<<9 : 0xd<<9; - o2 |= (REGTMPT<<6) | (r<<3) | rt; - break; - case 50: /* ADWORD */ - thumbaclass(&p->from, p); - o1 = instoffset&0xffff; - o2 = (instoffset>>16)&0xffff; - thumbaclass(&p->to, p); - o3 = instoffset&0xffff; - o4 = (instoffset>>16)&0xffff; - break; - case 51: /* BL r */ - o1 = mvlh(REGPC, REGLINK); // mov pc, lr - o2 = mvlh(rt, REGPC); // mov r, pc - break; - } - - v = p->pc; - switch(o->size) { - default: - if(debug['a']) - Bprint(&bso, " %.8ux:\t\t%P\n", v, p); - break; - case 2: - if(debug['a']) - Bprint(&bso, " %.8ux: %.8ux\t%P\n", v, o1, p); - hputl(o1); - break; - case 4: - if(debug['a']) - Bprint(&bso, " %.8ux: %.8ux %.8ux\t%P\n", v, o1, o2, p); - hputl(o1); - hputl(o2); - break; - case 6: - if(debug['a']) - Bprint(&bso, "%.8ux: %.8ux %.8ux %.8ux\t%P\n", v, o1, o2, o3, p); - hputl(o1); - hputl(o2); - hputl(o3); - break; - case 8: - if(debug['a']) - Bprint(&bso, "%.8ux: %.8ux %.8ux %.8ux %.8ux\t%P\n", v, o1, o2, o3, o4, p); - hputl(o1); - hputl(o2); - hputl(o3); - hputl(o4); - break; - case 10: - if(debug['a']) - Bprint(&bso, "%.8ux: %.8ux %.8ux %.8ux %.8ux %.8ux\t%P\n", v, o1, o2, o3, o4, o5, p); - hputl(o1); - hputl(o2); - hputl(o3); - hputl(o4); - hputl(o5); - break; - case 12: - if(debug['a']) - Bprint(&bso, "%.8ux: %.8ux %.8ux %.8ux %.8ux %.8ux %.8ux\t%P\n", v, o1, o2, o3, o4, o5, o6, p); - hputl(o1); - hputl(o2); - hputl(o3); - hputl(o4); - hputl(o5); - hputl(o6); - break; - case 14: - if(debug['a']) - Bprint(&bso, "%.8ux: %.8ux %.8ux %.8ux %.8ux %.8ux %.8ux %.8ux\t%P\n", v, o1, o2, o3, o4, o5, o6, o7, p); - hputl(o1); - hputl(o2); - hputl(o3); - hputl(o4); - hputl(o5); - hputl(o6); - hputl(o7); - break; - } - if(debug['G']){ - if(o->type == 17){ - print("%x: word %d\n", p->pc, (o2<<16)+o1); - return; - } - if(o->type == 50){ - print("%x: word %d\n", p->pc, (o2<<16)+o1); - print("%x: word %d\n", p->pc, (o4<<16)+o3); - return; - } - if(o->size > 0) dis(o1, p->pc); - if(o->size > 2) dis(o2, p->pc+2); - if(o->size > 4) dis(o3, p->pc+4); - if(o->size > 6) dis(o4, p->pc+6); - if(o->size > 8) dis(o5, p->pc+8); - if(o->size > 10) dis(o6, p->pc+10); - if(o->size > 12) dis(o7, p->pc+12); - // if(o->size > 14) dis(o8, p->pc+14); - } -} - -static int32 -thumboprr(int a) -{ - switch(a) { - case AMVN: return 0xf<<6; - case ACMP: return 0xa<<6; - case ACMN: return 0xb<<6; - case ATST: return 0x8<<6; - case AADC: return 0x5<<6; - case ASBC: return 0x6<<6; - case AMUL: - case AMULU: return 0xd<<6; - case AAND: return 0x0<<6; - case AEOR: return 0x1<<6; - case AORR: return 0xc<<6; - case ABIC: return 0xe<<6; - case ASRL: return 0x3<<6; - case ASRA: return 0x4<<6; - case ASLL: return 0x2<<6; - } - diag("bad thumbop oprr %d", a); - prasm(curp); - return 0; -} - -static int32 -thumbopirr(int a, int ld) -{ - if(ld) - diag("load in thumbopirr"); - switch(a){ - case AMOVW: return 0xc<<11; - case AMOVH: - case AMOVHU: return 0x10<<11; - case AMOVB: - case AMOVBU: return 0xe<<11; - } - return 0; -} - -static int32 -thumboprrr(int a, int ld) -{ - if(ld){ - switch(a){ - case AMOVW: return 0x2c<<9; - case AMOVH: return 0x2f<<9; - case AMOVB: return 0x2b<<9; - case AMOVHU: return 0x2d<<9; - case AMOVBU: return 0x2e<<9; - } - } - else{ - switch(a){ - case AMOVW: return 0x28<<9; - case AMOVHU: - case AMOVH: return 0x29<<9; - case AMOVBU: - case AMOVB: return 0x2a<<9; - } - } - diag("bad thumbop oprrr %d", a); - prasm(curp); - return 0; -} - -static int32 -thumbopri(int a) -{ - switch(a) { - case ASRL: return 0x1<<11; - case ASRA: return 0x2<<11; - case ASLL: return 0x0<<11; - case AADD: return 0x2<<11; - case ASUB: return 0x3<<11; - case AMOVW: return 0x0<<11; - case ACMP: return 0x1<<11; - } - diag("bad thumbop opri %d", a); - prasm(curp); - return 0; -} - -static int32 -thumbophh(int a) -{ - switch(a) { - case AADD: return 0x0<<8; - case AMOVW: return 0x2<<8; - case ACMP: return 0x1<<8; - } - diag("bad thumbop ophh %d", a); - prasm(curp); - return 0; -} - -static int32 -thumbopbra(int a) -{ - switch(a) { - case ABEQ: return 0x0<<8; - case ABNE: return 0x1<<8; - case ABCS: return 0x2<<8; - case ABHS: return 0x2<<8; - case ABCC: return 0x3<<8; - case ABLO: return 0x3<<8; - case ABMI: return 0x4<<8; - case ABPL: return 0x5<<8; - case ABVS: return 0x6<<8; - case ABVC: return 0x7<<8; - case ABHI: return 0x8<<8; - case ABLS: return 0x9<<8; - case ABGE: return 0xa<<8; - case ABLT: return 0xb<<8; - case ABGT: return 0xc<<8; - case ABLE: return 0xd<<8; - } - diag("bad thumbop opbra %d", a); - prasm(curp); - return 0; -} - -static int32 -thumbopmv(int a, int ld) -{ - switch(a) { - case AMOVW: return (ld ? 0xd : 0xc)<<11; - case AMOVH: - case AMOVHU: return (ld ? 0x11: 0x10)<<11; - case AMOVB: - case AMOVBU: return (ld ? 0xf : 0xe)<<11; - } - diag("bad thumbop opmv %d", a); - prasm(curp); - return 0; -} - -static void -lowreg(Prog *p, int r) -{ - if(high(r)) - diag("high reg [%P]", p); -} - -static void -mult(Prog *p, int n, int m) -{ - if(m*(n/m) != n) - diag("%d not M(%d) [%P]", n, m, p); -} - -static void -numr(Prog *p, int n, int min, int max) -{ - if(n < min || n > max) - diag("%d not in %d-%d [%P]", n, min, max, p); -} - -static void -regis(Prog *p, int r, int r1, int r2) -{ - if(r != r1 && r != r2) - diag("reg %d not %d or %d [%P]", r, r1, r2, p); -} - -void -hputl(int n) -{ - cbp[1] = n>>8; - cbp[0] = n; - cbp += 2; - cbc -= 2; - if(cbc <= 0) - cflush(); -} - -void -thumbcount() -{ - int i, c = 0, t = 0; - - for (i = 0; i < OPCNTSZ; i++) - t += opcount[i]; - if(t == 0) - return; - for (i = 0; i < OPCNTSZ; i++){ - c += opcount[i]; - print("%d: %d %d %d%%\n", i, opcount[i], c, (opcount[i]*100+t/2)/t); - } -} - -char *op1[] = { "lsl", "lsr", "asr" }; -char *op2[] = { "add", "sub" }; -char *op3[] = { "movw", "cmp", "add", "sub" }; -char *op4[] = { "and", "eor", "lsl", "lsr", "asr", "adc", "sbc", "ror", - "tst", "neg", "cmp", "cmpn", "or", "mul", "bitc", "movn" }; -char *op5[] = { "add", "cmp", "movw", "bx" }; -char *op6[] = { "smovw", "smovh", "smovb", "lmovb", "lmovw", "lmovhu", "lmovbu", "lmovh" }; -char *op7[] = { "smovw", "lmovw", "smovb", "lmovbu" }; -char *op8[] = { "smovh", "lmovhu" }; -char *op9[] = { "smovw", "lmovw" }; -char *op10[] = { "push", "pop" }; -char *op11[] = { "stmia", "ldmia" }; - -char *cond[] = { "eq", "ne", "hs", "lo", "mi", "pl", "vs", "vc", - "hi", "ls", "ge", "lt", "gt", "le", "al", "nv" }; - -#define B(h, l) bits(i, h, l) -#define IMM(h, l) B(h, l) -#define REG(h, l) reg(B(h, l)) -#define LHREG(h, l, lh) lhreg(B(h, l), B(lh, lh)) -#define COND(h, l) cond[B(h, l)] -#define OP1(h, l) op1[B(h, l)] -#define OP2(h, l) op2[B(h, l)] -#define OP3(h, l) op3[B(h, l)] -#define OP4(h, l) op4[B(h, l)] -#define OP5(h, l) op5[B(h, l)] -#define OP6(h, l) op6[B(h, l)] -#define OP7(h, l) op7[B(h, l)] -#define OP8(h, l) op8[B(h, l)] -#define OP9(h, l) op9[B(h, l)] -#define OP10(h, l) op10[B(h, l)] -#define OP11(h, l) op11[B(h, l)] -#define SBZ(h, l) if(IMM(h, l) != 0) diag("%x: %x bits %d,%d not zero", pc, i, h, l) -#define SNBZ(h, l) if(IMM(h, l) == 0) diag("%x: %x bits %d,%d zero", pc, i, h, l) -#define SBO(h, l) if(IMM(h, l) != 1) diag("%x: %x bits %d,%d not one", pc, i, h, l) - -static int -bits(int i, int h, int l) -{ - if(h < l) - diag("h < l in bits"); - return (i&(((1<<(h-l+1))-1)<<l))>>l; -} - -static char * -reg(int r) -{ - static char s[4][4]; - static int i = 0; - - if(r < 0 || r > 7) - diag("register %d out of range", r); - i++; - if(i == 4) - i = 0; - sprint(s[i], "r%d", r); - return s[i]; -} - -static char *regnames[] = { "sp", "lr", "pc" }; - -static char * -lhreg(int r, int lh) -{ - static char s[4][4]; - static int i = 0; - - if(lh == 0) - return reg(r); - if(r < 0 || r > 7) - diag("high register %d out of range", r); - i++; - if(i == 4) - i = 0; - if(r >= 5) - sprint(s[i], "%s", regnames[r-5]); - else - sprint(s[i], "r%d", r+8); - return s[i]; -} - -static void -illegal(int i, int pc) -{ - diag("%x: %x illegal instruction", pc, i); -} - -static void -dis(int i, int pc) -{ - static int lasto; - int o, l; - char *op; - - print("%x: %x: ", pc, i); - if(i&0xffff0000) - illegal(i, pc); - o = B(15, 13); - switch(o){ - case 0: - o = B(12, 11); - switch(o){ - case 0: - case 1: - case 2: - print("%s %d, %s, %s\n", OP1(12, 11), IMM(10, 6), REG(5, 3), REG(2, 0)); - return; - case 3: - if(B(10, 10) == 0) - print("%s %s, %s, %s\n", OP2(9, 9), REG(8, 6), REG(5, 3), REG(2, 0)); - else - print("%s %d, %s, %s\n", OP2(9, 9), IMM(8, 6), REG(5, 3), REG(2, 0)); - return; - } - case 1: - print("%s %d, %s\n", OP3(12, 11), IMM(7, 0), REG(10, 8)); - return; - case 2: - o = B(12, 10); - if(o == 0){ - print("%s %s, %s\n", OP4(9, 6), REG(5, 3), REG(2, 0)); - return; - } - if(o == 1){ - o = B(9, 8); - if(o == 3){ - SBZ(7, 7); - SBZ(2, 0); - print("%s %s\n", OP5(9, 8), LHREG(5, 3, 6)); - return; - } - SNBZ(7, 6); - print("%s %s, %s\n", OP5(9, 8), LHREG(5, 3, 6), LHREG(2, 0, 7)); - return; - } - if(o == 2 || o == 3){ - print("movw %d(pc)[%x], %s\n", 4*IMM(7, 0), 4*IMM(7, 0)+pc+4, REG(10, 8)); - return; - } - op = OP6(11, 9); - if(*op == 'l') - print("%s [%s, %s], %s\n", op+1, REG(8, 6), REG(5, 3), REG(2, 0)); - else - print("%s %s, [%s, %s]\n", op+1, REG(2, 0), REG(8, 6), REG(5, 3)); - return; - case 3: - op = OP7(12, 11); - if(B(12, 11) == 0 || B(12,11) == 1) - l = 4; - else - l = 1; - if(*op == 'l') - print("%s %d(%s), %s\n", op+1, l*IMM(10, 6), REG(5, 3), REG(2, 0)); - else - print("%s %s, %d(%s)\n", op+1, REG(2, 0), l*IMM(10, 6), REG(5, 3)); - return; - case 4: - if(B(12, 12) == 0){ - op = OP8(11, 11); - if(*op == 'l') - print("%s %d(%s), %s\n", op+1, 2*IMM(10, 6), REG(5, 3), REG(2, 0)); - else - print("%s %s, %d(%s)\n", op+1, REG(2, 0), 2*IMM(10, 6), REG(5, 3)); - return; - } - op = OP9(11, 11); - if(*op == 'l') - print("%s %d(sp), %s\n", op+1, 4*IMM(7, 0), REG(10, 8)); - else - print("%s %s, %d(sp)\n", op+1, REG(10, 8), 4*IMM(7, 0)); - return; - case 5: - if(B(12, 12) == 0){ - if(B(11, 11) == 0) - print("add %d, pc, %s\n", 4*IMM(7, 0), REG(10, 8)); - else - print("add %d, sp, %s\n", 4*IMM(7, 0), REG(10, 8)); - return; - } - if(B(11, 8) == 0){ - print("%s %d, sp\n", OP2(7, 7), 4*IMM(6, 0)); - return; - } - SBO(10, 10); - SBZ(9, 9); - if(B(8, 8) == 0) - print("%s sp, %d\n", OP10(11, 11), IMM(7, 0)); - else - print("%s sp, %d|15\n", OP10(11, 11), IMM(7, 0)); - return; - case 6: - if(B(12, 12) == 0){ - print("%s %s, %d\n", OP11(11, 11), REG(10, 8), IMM(7, 0)); - return; - } - if(B(11, 8) == 0xf){ - print("swi %d\n", IMM(7, 0)); - return; - } - o = IMM(7, 0); - if(o&0x80) - o |= 0xffffff00; - o = pc+4+(o<<1); - print("b%s %x\n", COND(11, 8), o); - return; - case 7: - o = B(12, 11); - switch(o){ - case 0: - o = IMM(10, 0); - if(o&0x400) - o |= 0xfffff800; - o = pc+4+(o<<1); - print("b %x\n", o); - return; - case 1: - illegal(i, pc); - return; - case 2: - lasto = IMM(10, 0); - print("bl\n"); - return; - case 3: - if(lasto&0x400) - lasto |= 0xfffff800; - o = IMM(10, 0); - o = (pc-2)+4+(o<<1)+(lasto<<12); - print("bl %x\n", o); - return; - } - } -} diff --git a/src/cmd/6g/gg.h b/src/cmd/6g/gg.h index 7efb2c252..2493771a0 100644 --- a/src/cmd/6g/gg.h +++ b/src/cmd/6g/gg.h @@ -23,6 +23,7 @@ struct Addr Sym* gotype; Sym* sym; + Node* node; int width; uchar type; uchar index; diff --git a/src/cmd/6g/gsubr.c b/src/cmd/6g/gsubr.c index ed98d1bc9..ae6ae5765 100644 --- a/src/cmd/6g/gsubr.c +++ b/src/cmd/6g/gsubr.c @@ -988,7 +988,7 @@ naddr(Node *n, Addr *a, int canemitcode) a->index = D_NONE; a->type = D_NONE; a->gotype = S; - + a->node = N; if(n == N) return; @@ -1067,6 +1067,8 @@ naddr(Node *n, Addr *a, int canemitcode) break; case PAUTO: a->type = D_AUTO; + if (n->sym) + a->node = n->orig; break; case PPARAM: case PPARAMOUT: diff --git a/src/cmd/6g/reg.c b/src/cmd/6g/reg.c index b4b5b7d6b..af9b29cbc 100644 --- a/src/cmd/6g/reg.c +++ b/src/cmd/6g/reg.c @@ -33,6 +33,8 @@ #define EXTERN #include "opt.h" +#define NREGVAR 32 /* 16 general + 16 floating */ +#define REGBITS ((uint32)0xffffffff) #define P2R(p) (Reg*)(p->reg) static int first = 1; @@ -114,6 +116,41 @@ setaddrs(Bits bit) } } +static char* regname[] = { + ".AX", + ".CX", + ".DX", + ".BX", + ".SP", + ".BP", + ".SI", + ".DI", + ".R8", + ".R9", + ".R10", + ".R11", + ".R12", + ".R13", + ".R14", + ".R15", + ".X0", + ".X1", + ".X2", + ".X3", + ".X4", + ".X5", + ".X6", + ".X7", + ".X8", + ".X9", + ".X10", + ".X11", + ".X12", + ".X13", + ".X14", + ".X15", +}; + void regopt(Prog *firstp) { @@ -143,6 +180,17 @@ regopt(Prog *firstp) firstr = R; lastr = R; nvar = 0; + + /* + * control flow is more complicated in generated go code + * than in generated c code. define pseudo-variables for + * registers, so we have complete register usage information. + */ + nvar = NREGVAR; + memset(var, 0, NREGVAR*sizeof var[0]); + for(i=0; i<NREGVAR; i++) + var[i].sym = lookup(regname[i]); + regbits = RtoB(D_SP); for(z=0; z<BITS; z++) { externs.b[z] = 0; @@ -247,6 +295,9 @@ regopt(Prog *firstp) case ACOMISD: case AUCOMISS: case AUCOMISD: + case ATESTB: + case ATESTL: + case ATESTQ: for(z=0; z<BITS; z++) r->use2.b[z] |= bit.b[z]; break; @@ -254,6 +305,7 @@ regopt(Prog *firstp) /* * right side write */ + case ALEAQ: case ANOP: case AMOVL: case AMOVQ: @@ -261,6 +313,8 @@ regopt(Prog *firstp) case AMOVW: case AMOVBLSX: case AMOVBLZX: + case AMOVBWSX: + case AMOVBWZX: case AMOVBQSX: case AMOVBQZX: case AMOVLQSX: @@ -269,6 +323,7 @@ regopt(Prog *firstp) case AMOVWLZX: case AMOVWQSX: case AMOVWQZX: + case APOPQ: case AMOVSS: case AMOVSD: @@ -357,6 +412,8 @@ regopt(Prog *firstp) case AIMULL: case AIMULQ: case AIMULW: + case ANEGB: + case ANEGW: case ANEGL: case ANEGQ: case ANOTL: @@ -366,6 +423,23 @@ regopt(Prog *firstp) case ASBBL: case ASBBQ: + case ASETCC: + case ASETCS: + case ASETEQ: + case ASETGE: + case ASETGT: + case ASETHI: + case ASETLE: + case ASETLS: + case ASETLT: + case ASETMI: + case ASETNE: + case ASETOC: + case ASETOS: + case ASETPC: + case ASETPL: + case ASETPS: + case AXCHGB: case AXCHGW: case AXCHGL: @@ -411,32 +485,44 @@ regopt(Prog *firstp) if(p->to.type != D_NONE) break; - case AIDIVB: case AIDIVL: - case AIDIVQ: case AIDIVW: - case AIMULB: - case ADIVB: + case AIDIVQ: case ADIVL: - case ADIVQ: case ADIVW: - case AMULB: + case ADIVQ: case AMULL: - case AMULQ: case AMULW: + case AMULQ: + r->set.b[0] |= RtoB(D_AX) | RtoB(D_DX); + r->use1.b[0] |= RtoB(D_AX) | RtoB(D_DX); + break; + + case AIDIVB: + case AIMULB: + case ADIVB: + case AMULB: + r->set.b[0] |= RtoB(D_AX); + r->use1.b[0] |= RtoB(D_AX); + break; case ACWD: - case ACDQ: - case ACQO: - r->regu |= RtoB(D_AX) | RtoB(D_DX); + r->set.b[0] |= RtoB(D_AX) | RtoB(D_DX); + r->use1.b[0] |= RtoB(D_AX); break; + case ACDQ: + r->set.b[0] |= RtoB(D_DX); + r->use1.b[0] |= RtoB(D_AX); + break; + case AREP: case AREPN: case ALOOP: case ALOOPEQ: case ALOOPNE: - r->regu |= RtoB(D_CX); + r->set.b[0] |= RtoB(D_CX); + r->use1.b[0] |= RtoB(D_CX); break; case AMOVSB: @@ -447,7 +533,8 @@ regopt(Prog *firstp) case ACMPSL: case ACMPSQ: case ACMPSW: - r->regu |= RtoB(D_SI) | RtoB(D_DI); + r->set.b[0] |= RtoB(D_SI) | RtoB(D_DI); + r->use1.b[0] |= RtoB(D_SI) | RtoB(D_DI); break; case ASTOSB: @@ -458,16 +545,22 @@ regopt(Prog *firstp) case ASCASL: case ASCASQ: case ASCASW: - r->regu |= RtoB(D_AX) | RtoB(D_DI); + r->set.b[0] |= RtoB(D_DI); + r->use1.b[0] |= RtoB(D_AX) | RtoB(D_DI); break; case AINSB: case AINSL: case AINSW: + r->set.b[0] |= RtoB(D_DX) | RtoB(D_DI); + r->use1.b[0] |= RtoB(D_DI); + break; + case AOUTSB: case AOUTSL: case AOUTSW: - r->regu |= RtoB(D_DI) | RtoB(D_DX); + r->set.b[0] |= RtoB(D_DI); + r->use1.b[0] |= RtoB(D_DX) | RtoB(D_DI); break; } } @@ -574,6 +667,24 @@ loop2: dumpit("pass4", firstr); /* + * pass 4.5 + * move register pseudo-variables into regu. + */ + for(r = firstr; r != R; r = r->link) { + r->regu = (r->refbehind.b[0] | r->set.b[0]) & REGBITS; + + r->set.b[0] &= ~REGBITS; + r->use1.b[0] &= ~REGBITS; + r->use2.b[0] &= ~REGBITS; + r->refbehind.b[0] &= ~REGBITS; + r->refahead.b[0] &= ~REGBITS; + r->calbehind.b[0] &= ~REGBITS; + r->calahead.b[0] &= ~REGBITS; + r->regdiff.b[0] &= ~REGBITS; + r->act.b[0] &= ~REGBITS; + } + + /* * pass 5 * isolate regions * calculate costs (paint1) @@ -726,6 +837,7 @@ addmove(Reg *r, int bn, int rn, int f) a->etype = v->etype; a->type = v->name; a->gotype = v->gotype; + a->node = v->node; // need to clean this up with wptr and // some of the defaults @@ -818,6 +930,7 @@ mkvar(Reg *r, Adr *a) { Var *v; int i, t, n, et, z, w, flag; + uint32 regu; int32 o; Bits bit; Sym *s; @@ -829,14 +942,17 @@ mkvar(Reg *r, Adr *a) if(t == D_NONE) goto none; - if(r != R) { - r->regu |= doregbits(t); - r->regu |= doregbits(a->index); - } + if(r != R) + r->use1.b[0] |= doregbits(a->index); switch(t) { default: - goto none; + regu = doregbits(t); + if(regu == 0) + goto none; + bit = zbits; + bit.b[0] = regu; + return bit; case D_ADDR: a->type = a->index; @@ -906,6 +1022,7 @@ mkvar(Reg *r, Adr *a) v->etype = et; v->width = w; v->addr = flag; // funny punning + v->node = a->node; if(debug['R']) print("bit=%2d et=%2d w=%d %S %D\n", i, et, w, s, a); diff --git a/src/cmd/6l/asm.c b/src/cmd/6l/asm.c index 320f4c9e9..4c04112b7 100644 --- a/src/cmd/6l/asm.c +++ b/src/cmd/6l/asm.c @@ -698,7 +698,7 @@ asmb(void) { int32 magic; int a, dynsym; - vlong vl, startva, symo, elfsymo, elfstro, elfsymsize, machlink; + vlong vl, startva, symo, machlink; ElfEhdr *eh; ElfPhdr *ph, *pph; ElfShdr *sh; @@ -709,9 +709,6 @@ asmb(void) Bflush(&bso); elftextsh = 0; - elfsymsize = 0; - elfstro = 0; - elfsymo = 0; if(debug['v']) Bprint(&bso, "%5.2f codeblk\n", cputime()); @@ -790,36 +787,13 @@ asmb(void) symo = rnd(symo, PEFILEALIGN); break; } + seek(cout, symo, 0); switch(HEADTYPE) { default: if(iself) { - /* - * the symbol information is stored as - * 32-bit symbol table size - * 32-bit line number table size - * symbol table - * line number table - */ - seek(cout, symo+8, 0); - if(debug['v']) - Bprint(&bso, "%5.2f sp\n", cputime()); - Bflush(&bso); - if(debug['v']) - Bprint(&bso, "%5.2f pc\n", cputime()); - Bflush(&bso); - if(!debug['s']) - strnput("", INITRND-(8+symsize+lcsize)%INITRND); - cflush(); seek(cout, symo, 0); - lputl(symsize); - lputl(lcsize); + asmelfsym(); cflush(); - elfsymo = symo+8+symsize+lcsize; - seek(cout, elfsymo, 0); - asmelfsym64(); - cflush(); - elfstro = seek(cout, 0, 1); - elfsymsize = elfstro - elfsymo; ewrite(cout, elfstrdat, elfstrsize); if(debug['v']) @@ -830,7 +804,6 @@ asmb(void) break; case Hdarwin: case Hwindows: - seek(cout, symo, 0); if(debug['v']) Bprint(&bso, "%5.2f dwarf\n", cputime()); @@ -1054,15 +1027,15 @@ asmb(void) sh = newElfShdr(elfstr[ElfStrSymtab]); sh->type = SHT_SYMTAB; - sh->off = elfsymo; - sh->size = elfsymsize; + sh->off = symo; + sh->size = symsize; sh->addralign = 8; sh->entsize = 24; sh->link = eh->shnum; // link to strtab sh = newElfShdr(elfstr[ElfStrStrtab]); sh->type = SHT_STRTAB; - sh->off = elfstro; + sh->off = symo+symsize; sh->size = elfstrsize; sh->addralign = 1; @@ -1149,6 +1122,10 @@ genasmsym(void (*put)(Sym*, char*, int, vlong, vlong, int, Sym*)) Auto *a; Sym *s; + s = lookup("etext", 0); + if(s->type == STEXT) + put(s, s->name, 'T', s->value, s->size, s->version, 0); + for(s=allsym; s!=S; s=s->allsym) { if(s->hide) continue; diff --git a/src/cmd/8g/gg.h b/src/cmd/8g/gg.h index 57cd1b56b..7da60d767 100644 --- a/src/cmd/8g/gg.h +++ b/src/cmd/8g/gg.h @@ -25,6 +25,7 @@ struct Addr Sym* gotype; Sym* sym; + Node* node; int width; uchar type; uchar index; diff --git a/src/cmd/8g/gsubr.c b/src/cmd/8g/gsubr.c index 5ad35fdce..6bcc3eed8 100644 --- a/src/cmd/8g/gsubr.c +++ b/src/cmd/8g/gsubr.c @@ -1720,6 +1720,7 @@ naddr(Node *n, Addr *a, int canemitcode) a->index = D_NONE; a->type = D_NONE; a->gotype = S; + a->node = N; if(n == N) return; @@ -1777,6 +1778,8 @@ naddr(Node *n, Addr *a, int canemitcode) break; case PAUTO: a->type = D_AUTO; + if (n->sym) + a->node = n->orig; break; case PPARAM: case PPARAMOUT: diff --git a/src/cmd/8g/reg.c b/src/cmd/8g/reg.c index 1465d372c..a2f3def37 100644 --- a/src/cmd/8g/reg.c +++ b/src/cmd/8g/reg.c @@ -33,6 +33,8 @@ #define EXTERN #include "opt.h" +#define NREGVAR 8 +#define REGBITS ((uint32)0xff) #define P2R(p) (Reg*)(p->reg) static int first = 1; @@ -114,6 +116,8 @@ setaddrs(Bits bit) } } +static char* regname[] = { ".ax", ".cx", ".dx", ".bx", ".sp", ".bp", ".si", ".di" }; + void regopt(Prog *firstp) { @@ -142,7 +146,17 @@ regopt(Prog *firstp) r1 = R; firstr = R; lastr = R; - nvar = 0; + + /* + * control flow is more complicated in generated go code + * than in generated c code. define pseudo-variables for + * registers, so we have complete register usage information. + */ + nvar = NREGVAR; + memset(var, 0, NREGVAR*sizeof var[0]); + for(i=0; i<NREGVAR; i++) + var[i].sym = lookup(regname[i]); + regbits = RtoB(D_SP); for(z=0; z<BITS; z++) { externs.b[z] = 0; @@ -249,14 +263,19 @@ regopt(Prog *firstp) /* * right side write */ + case AFSTSW: + case ALEAL: case ANOP: case AMOVL: case AMOVB: case AMOVW: case AMOVBLSX: case AMOVBLZX: + case AMOVBWSX: + case AMOVBWZX: case AMOVWLSX: case AMOVWLZX: + case APOPL: for(z=0; z<BITS; z++) r->set.b[z] |= bit.b[z]; break; @@ -321,6 +340,23 @@ regopt(Prog *firstp) case AADCL: case ASBBL: + case ASETCC: + case ASETCS: + case ASETEQ: + case ASETGE: + case ASETGT: + case ASETHI: + case ASETLE: + case ASETLS: + case ASETLT: + case ASETMI: + case ASETNE: + case ASETOC: + case ASETOS: + case ASETPC: + case ASETPL: + case ASETPS: + case AXCHGB: case AXCHGW: case AXCHGL: @@ -349,20 +385,32 @@ regopt(Prog *firstp) if(p->to.type != D_NONE) break; - case AIDIVB: case AIDIVL: case AIDIVW: - case AIMULB: - case ADIVB: case ADIVL: case ADIVW: - case AMULB: case AMULL: case AMULW: + r->set.b[0] |= RtoB(D_AX) | RtoB(D_DX); + r->use1.b[0] |= RtoB(D_AX) | RtoB(D_DX); + break; + + case AIDIVB: + case AIMULB: + case ADIVB: + case AMULB: + r->set.b[0] |= RtoB(D_AX); + r->use1.b[0] |= RtoB(D_AX); + break; case ACWD: + r->set.b[0] |= RtoB(D_AX) | RtoB(D_DX); + r->use1.b[0] |= RtoB(D_AX); + break; + case ACDQ: - r->regu |= RtoB(D_AX) | RtoB(D_DX); + r->set.b[0] |= RtoB(D_DX); + r->use1.b[0] |= RtoB(D_AX); break; case AREP: @@ -370,7 +418,8 @@ regopt(Prog *firstp) case ALOOP: case ALOOPEQ: case ALOOPNE: - r->regu |= RtoB(D_CX); + r->set.b[0] |= RtoB(D_CX); + r->use1.b[0] |= RtoB(D_CX); break; case AMOVSB: @@ -379,7 +428,8 @@ regopt(Prog *firstp) case ACMPSB: case ACMPSL: case ACMPSW: - r->regu |= RtoB(D_SI) | RtoB(D_DI); + r->set.b[0] |= RtoB(D_SI) | RtoB(D_DI); + r->use1.b[0] |= RtoB(D_SI) | RtoB(D_DI); break; case ASTOSB: @@ -388,16 +438,22 @@ regopt(Prog *firstp) case ASCASB: case ASCASL: case ASCASW: - r->regu |= RtoB(D_AX) | RtoB(D_DI); + r->set.b[0] |= RtoB(D_DI); + r->use1.b[0] |= RtoB(D_AX) | RtoB(D_DI); break; case AINSB: case AINSL: case AINSW: + r->set.b[0] |= RtoB(D_DX) | RtoB(D_DI); + r->use1.b[0] |= RtoB(D_DI); + break; + case AOUTSB: case AOUTSL: case AOUTSW: - r->regu |= RtoB(D_DI) | RtoB(D_DX); + r->set.b[0] |= RtoB(D_DI); + r->use1.b[0] |= RtoB(D_DX) | RtoB(D_DI); break; } } @@ -504,6 +560,24 @@ loop2: dumpit("pass4", firstr); /* + * pass 4.5 + * move register pseudo-variables into regu. + */ + for(r = firstr; r != R; r = r->link) { + r->regu = (r->refbehind.b[0] | r->set.b[0]) & REGBITS; + + r->set.b[0] &= ~REGBITS; + r->use1.b[0] &= ~REGBITS; + r->use2.b[0] &= ~REGBITS; + r->refbehind.b[0] &= ~REGBITS; + r->refahead.b[0] &= ~REGBITS; + r->calbehind.b[0] &= ~REGBITS; + r->calahead.b[0] &= ~REGBITS; + r->regdiff.b[0] &= ~REGBITS; + r->act.b[0] &= ~REGBITS; + } + + /* * pass 5 * isolate regions * calculate costs (paint1) @@ -656,6 +730,7 @@ addmove(Reg *r, int bn, int rn, int f) a->etype = v->etype; a->type = v->name; a->gotype = v->gotype; + a->node = v->node; // need to clean this up with wptr and // some of the defaults @@ -732,7 +807,7 @@ Bits mkvar(Reg *r, Adr *a) { Var *v; - int i, t, n, et, z, w, flag; + int i, t, n, et, z, w, flag, regu; int32 o; Bits bit; Sym *s; @@ -744,14 +819,17 @@ mkvar(Reg *r, Adr *a) if(t == D_NONE) goto none; - if(r != R) { - r->regu |= doregbits(t); - r->regu |= doregbits(a->index); - } + if(r != R) + r->use1.b[0] |= doregbits(a->index); switch(t) { default: - goto none; + regu = doregbits(t); + if(regu == 0) + goto none; + bit = zbits; + bit.b[0] = regu; + return bit; case D_ADDR: a->type = a->index; @@ -821,6 +899,7 @@ mkvar(Reg *r, Adr *a) v->etype = et; v->width = w; v->addr = flag; // funny punning + v->node = a->node; if(debug['R']) print("bit=%2d et=%2d w=%d %S %D flag=%d\n", i, et, w, s, a, v->addr); diff --git a/src/cmd/8l/asm.c b/src/cmd/8l/asm.c index c426a88a4..cb900d28d 100644 --- a/src/cmd/8l/asm.c +++ b/src/cmd/8l/asm.c @@ -663,7 +663,7 @@ asmb(void) { int32 v, magic; int a, dynsym; - uint32 symo, startva, machlink, elfsymo, elfstro, elfsymsize; + uint32 symo, startva, machlink; ElfEhdr *eh; ElfPhdr *ph, *pph; ElfShdr *sh; @@ -675,10 +675,6 @@ asmb(void) Bprint(&bso, "%5.2f asmb\n", cputime()); Bflush(&bso); - elfsymsize = 0; - elfstro = 0; - elfsymo = 0; - sect = segtext.sect; seek(cout, sect->vaddr - segtext.vaddr + segtext.fileoff, 0); codeblk(sect->vaddr, sect->len); @@ -724,10 +720,10 @@ asmb(void) if(iself) goto Elfsym; case Hgarbunix: - seek(cout, rnd(HEADR+segtext.filelen, 8192)+segdata.filelen, 0); + symo = rnd(HEADR+segtext.filelen, 8192)+segdata.filelen; break; case Hunixcoff: - seek(cout, rnd(HEADR+segtext.filelen, INITRND)+segdata.filelen, 0); + symo = rnd(HEADR+segtext.filelen, INITRND)+segdata.filelen; break; case Hplan9x32: symo = HEADR+segtext.filelen+segdata.filelen; @@ -749,17 +745,14 @@ asmb(void) symo = rnd(symo, PEFILEALIGN); break; } + seek(cout, symo, 0); switch(HEADTYPE) { default: if(iself) { if(debug['v']) Bprint(&bso, "%5.2f elfsym\n", cputime()); - elfsymo = symo+8+symsize+lcsize; - seek(cout, elfsymo, 0); - asmelfsym32(); + asmelfsym(); cflush(); - elfstro = seek(cout, 0, 1); - elfsymsize = elfstro - elfsymo; ewrite(cout, elfstrdat, elfstrsize); if(debug['v']) @@ -768,10 +761,9 @@ asmb(void) } break; case Hplan9x32: - seek(cout, symo, 0); asmplan9sym(); cflush(); - + sym = lookup("pclntab", 0); if(sym != nil) { lcsize = sym->np; @@ -783,7 +775,6 @@ asmb(void) break; case Hdarwin: case Hwindows: - seek(cout, symo, 0); if(debug['v']) Bprint(&bso, "%5.2f dwarf\n", cputime()); dwarfemitdebugsections(); @@ -1110,15 +1101,15 @@ asmb(void) sh = newElfShdr(elfstr[ElfStrSymtab]); sh->type = SHT_SYMTAB; - sh->off = elfsymo; - sh->size = elfsymsize; + sh->off = symo; + sh->size = symsize; sh->addralign = 4; sh->entsize = 16; sh->link = eh->shnum; // link to strtab sh = newElfShdr(elfstr[ElfStrStrtab]); sh->type = SHT_STRTAB; - sh->off = elfstro; + sh->off = symo+symsize; sh->size = elfstrsize; sh->addralign = 1; diff --git a/src/cmd/8l/l.h b/src/cmd/8l/l.h index 8f39ef519..7e7cd5d63 100644 --- a/src/cmd/8l/l.h +++ b/src/cmd/8l/l.h @@ -260,11 +260,15 @@ EXTERN union #define cbuf u.obuf #define xbuf u.ibuf -#pragma varargck type "A" uint +#pragma varargck type "A" int #pragma varargck type "D" Adr* +#pragma varargck type "I" int +#pragma varargck type "I" uchar* #pragma varargck type "P" Prog* #pragma varargck type "R" int #pragma varargck type "S" char* +#pragma varargck type "Y" Sym* +#pragma varargck type "i" char* EXTERN int32 HEADR; EXTERN int32 HEADTYPE; @@ -383,11 +387,6 @@ void deadcode(void); #define WPUT(a) wputl(a) #define VPUT(a) vputl(a) -#pragma varargck type "D" Adr* -#pragma varargck type "P" Prog* -#pragma varargck type "R" int -#pragma varargck type "A" int - /* Used by ../ld/dwarf.c */ enum { diff --git a/src/cmd/cgo/gcc.go b/src/cmd/cgo/gcc.go index fa7602cf2..10411e94f 100644 --- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -660,11 +660,14 @@ func (p *Package) gccName() (ret string) { } // gccMachine returns the gcc -m flag to use, either "-m32" or "-m64". -func (p *Package) gccMachine() string { - if p.PtrSize == 8 { - return "-m64" - } - return "-m32" +func (p *Package) gccMachine() []string { + switch runtime.GOARCH { + case "amd64": + return []string{"-m64"} + case "386": + return []string{"-m32"} + } + return nil } const gccTmp = "_obj/_cgo_.o" @@ -674,7 +677,6 @@ const gccTmp = "_obj/_cgo_.o" func (p *Package) gccCmd() []string { c := []string{ p.gccName(), - p.gccMachine(), "-Wall", // many warnings "-Werror", // warnings are errors "-o" + gccTmp, // write object to tmp @@ -684,6 +686,7 @@ func (p *Package) gccCmd() []string { "-xc", // input language is C } c = append(c, p.GccOptions...) + c = append(c, p.gccMachine()...) c = append(c, "-") //read input from standard input return c } @@ -719,7 +722,8 @@ func (p *Package) gccDebug(stdin []byte) *dwarf.Data { // #defines that gcc encountered while processing the input // and its included files. func (p *Package) gccDefines(stdin []byte) string { - base := []string{p.gccName(), p.gccMachine(), "-E", "-dM", "-xc"} + base := []string{p.gccName(), "-E", "-dM", "-xc"} + base = append(base, p.gccMachine()...) stdout, _ := runGcc(stdin, append(append(base, p.GccOptions...), "-")) return stdout } diff --git a/src/cmd/gc/dcl.c b/src/cmd/gc/dcl.c index 78c676346..83be82f92 100644 --- a/src/cmd/gc/dcl.c +++ b/src/cmd/gc/dcl.c @@ -438,20 +438,6 @@ newtype(Sym *s) return t; } -/* - * type check top level declarations - */ -void -dclchecks(void) -{ - NodeList *l; - - for(l=externdcl; l; l=l->next) { - if(l->n->op != ONAME) - continue; - typecheck(&l->n, Erv); - } -} /* * := declarations diff --git a/src/cmd/gc/gen.c b/src/cmd/gc/gen.c index 0b6f5bbd8..feb55e905 100644 --- a/src/cmd/gc/gen.c +++ b/src/cmd/gc/gen.c @@ -51,6 +51,8 @@ allocparams(void) } if(n->op != ONAME || n->class != PAUTO) continue; + if (n->xoffset != BADWIDTH) + continue; if(n->type == T) continue; dowidth(n->type); @@ -669,14 +671,18 @@ dotoffset(Node *n, int *oary, Node **nn) * make a new off the books */ void -tempname(Node *n, Type *t) +tempname(Node *nn, Type *t) { + Node *n; Sym *s; uint32 w; if(stksize < 0) fatal("tempname not during code generation"); + if (curfn == N) + fatal("no curfn for tempname"); + if(t == T) { yyerror("tempname called with nil type"); t = types[TINT32]; @@ -687,14 +693,15 @@ tempname(Node *n, Type *t) snprint(namebuf, sizeof(namebuf), "autotmp_%.4d", statuniqgen); statuniqgen++; s = lookup(namebuf); - memset(n, 0, sizeof(*n)); - n->op = ONAME; + n = nod(ONAME, N, N); n->sym = s; n->type = t; n->class = PAUTO; n->addable = 1; n->ullman = 1; n->noescape = 1; + n->curfn = curfn; + curfn->dcl = list(curfn->dcl, n); dowidth(t); w = t->width; @@ -703,5 +710,8 @@ tempname(Node *n, Type *t) if(thechar == '5') stksize = rnd(stksize, widthptr); n->xoffset = -stksize; - n->pun = anyregalloc(); + + // print("\ttmpname (%d): %N\n", stksize, n); + + *nn = *n; } diff --git a/src/cmd/gc/go.h b/src/cmd/gc/go.h index f5c0443f8..86db48391 100644 --- a/src/cmd/gc/go.h +++ b/src/cmd/gc/go.h @@ -225,7 +225,7 @@ struct Node Type* realtype; // as determined by typecheck NodeList* list; NodeList* rlist; - Node* orig; // original form, for printing + Node* orig; // original form, for printing, and tracking copies of ONAMEs // for-body NodeList* ninit; @@ -273,6 +273,7 @@ struct Node int32 lineno; int32 endlineno; vlong xoffset; + int32 stkdelta; // offset added by stack frame compaction phase. int32 ostk; int32 iota; }; @@ -547,6 +548,7 @@ struct Var vlong offset; Sym* sym; Sym* gotype; + Node* node; int width; char name; char etype; @@ -862,7 +864,6 @@ NodeList* checkarglist(NodeList *all, int input); Node* colas(NodeList *left, NodeList *right); void colasdefn(NodeList *left, Node *defn); NodeList* constiter(NodeList *vl, Node *t, NodeList *cl); -void dclchecks(void); Node* dclname(Sym *s); void declare(Node *n, int ctxt); Type* dostruct(NodeList *l, int et); @@ -1108,6 +1109,7 @@ int istype(Type *t, int et); void linehist(char *file, int32 off, int relative); NodeList* list(NodeList *l, Node *n); NodeList* list1(Node *n); +void listsort(NodeList**, int(*f)(Node*, Node*)); Node* liststmt(NodeList *l); NodeList* listtreecopy(NodeList *l); Sym* lookup(char *name); @@ -1166,6 +1168,11 @@ int exportassignok(Type *t, char *desc); int islvalue(Node *n); Node* typecheck(Node **np, int top); void typechecklist(NodeList *l, int top); +Node* typecheckdef(Node *n); +void resumetypecopy(void); +void copytype(Node *n, Type *t); +void defertypecopy(Node *n, Type *t); +void queuemethod(Node *n); /* * unsafe.c @@ -1177,15 +1184,10 @@ Node* unsafenmagic(Node *n); */ Node* callnew(Type *t); Node* chanfn(char *name, int n, Type *t); -void copytype(Node *n, Type *t); -void defertypecopy(Node *n, Type *t); Node* mkcall(char *name, Type *t, NodeList **init, ...); Node* mkcall1(Node *fn, Type *t, NodeList **init, ...); -void queuemethod(Node *n); -void resumetypecopy(void); int vmatch1(Node *l, Node *r); void walk(Node *fn); -Node* walkdef(Node *n); void walkexpr(Node **np, NodeList **init); void walkexprlist(NodeList *l, NodeList **init); void walkexprlistsafe(NodeList *l, NodeList **init); diff --git a/src/cmd/gc/go.y b/src/cmd/gc/go.y index fdaab4fa4..1278c2586 100644 --- a/src/cmd/gc/go.y +++ b/src/cmd/gc/go.y @@ -1792,24 +1792,12 @@ hidden_opt_sym: } hidden_dcl: - hidden_opt_sym hidden_type + hidden_opt_sym hidden_type hidden_tag { $$ = nod(ODCLFIELD, $1, typenod($2)); + $$->val = $3; } -| hidden_opt_sym LDDD - { - Type *t; - - yyerror("invalid variadic function type in import - recompile import"); - - t = typ(TARRAY); - t->bound = -1; - t->type = typ(TINTER); - $$ = nod(ODCLFIELD, $1, typenod(t)); - $$->isddd = 1; - } - -| hidden_opt_sym LDDD hidden_type +| hidden_opt_sym LDDD hidden_type hidden_tag { Type *t; @@ -1818,6 +1806,7 @@ hidden_dcl: t->type = $3; $$ = nod(ODCLFIELD, $1, typenod(t)); $$->isddd = 1; + $$->val = $4; } hidden_structdcl: diff --git a/src/cmd/gc/lex.c b/src/cmd/gc/lex.c index 5e2f73fc5..88acb60af 100644 --- a/src/cmd/gc/lex.c +++ b/src/cmd/gc/lex.c @@ -5,7 +5,7 @@ #define EXTERN #include "go.h" #include "y.tab.h" -#include <ar.h> +#include <ar.h> #undef getc #undef ungetc @@ -274,7 +274,9 @@ main(int argc, char *argv[]) funccompile(l->n, 1); } - dclchecks(); + for(l=externdcl; l; l=l->next) + if(l->n->op == ONAME) + typecheck(&l->n, Erv); if(nerrors) errorexit(); diff --git a/src/cmd/gc/pgen.c b/src/cmd/gc/pgen.c index 9bd845dde..ab6186697 100644 --- a/src/cmd/gc/pgen.c +++ b/src/cmd/gc/pgen.c @@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -#undef EXTERN -#define EXTERN -#include "gg.h" -#include "opt.h" +#include "gg.h" +#include "opt.h" + +static void compactframe(Prog* p); void compile(Node *fn) @@ -16,6 +16,7 @@ compile(Node *fn) int32 lno; Type *t; Iter save; + vlong oldstksize; if(newproc == N) { newproc = sysfunc("newproc"); @@ -109,11 +110,115 @@ compile(Node *fn) regopt(ptxt); } + oldstksize = stksize; + if(thechar != '5') + compactframe(ptxt); + if(0) + print("compactframe: %ld to %ld\n", oldstksize, stksize); + defframe(ptxt); - if(debug['f']) + if(0) frame(0); ret: lineno = lno; } + + +// Sort the list of stack variables. autos after anything else, +// within autos, unused after used, and within used on reverse alignment. +// non-autos sort on offset. +static int +cmpstackvar(Node *a, Node *b) +{ + if (a->class != b->class) + return (a->class == PAUTO) ? 1 : -1; + if (a->class != PAUTO) + return a->xoffset - b->xoffset; + if ((a->used == 0) != (b->used == 0)) + return b->used - a->used; + return b->type->align - a->type->align; + +} + +static void +compactframe(Prog* ptxt) +{ + NodeList *ll; + Node* n; + Prog *p; + uint32 w; + + if (stksize == 0) + return; + + // Mark the PAUTO's unused. + for(ll=curfn->dcl; ll != nil; ll=ll->next) + if (ll->n->class == PAUTO && ll->n->op == ONAME) + ll->n->used = 0; + + // Sweep the prog list to mark any used nodes. + for (p = ptxt; p; p = p->link) { + if (p->from.type == D_AUTO && p->from.node) + p->from.node->used++; + + if (p->to.type == D_AUTO && p->to.node) + p->to.node->used++; + } + + listsort(&curfn->dcl, cmpstackvar); + + // Unused autos are at the end, chop 'em off. + ll = curfn->dcl; + n = ll->n; + if (n->class == PAUTO && n->op == ONAME && !n->used) { + curfn->dcl = nil; + stksize = 0; + return; + } + + for(ll = curfn->dcl; ll->next != nil; ll=ll->next) { + n = ll->next->n; + if (n->class == PAUTO && n->op == ONAME && !n->used) { + ll->next = nil; + curfn->dcl->end = ll; + break; + } + } + + // Reassign stack offsets of the locals that are still there. + stksize = 0; + for(ll = curfn->dcl; ll != nil; ll=ll->next) { + n = ll->n; + // TODO find out where the literal autos come from + if (n->class != PAUTO || n->op != ONAME) + continue; + + w = n->type->width; + if((w >= MAXWIDTH) || (w < 1)) + fatal("bad width"); + stksize += w; + stksize = rnd(stksize, n->type->align); + if(thechar == '5') + stksize = rnd(stksize, widthptr); + n->stkdelta = -stksize - n->xoffset; + } + + // Fixup instructions. + for (p = ptxt; p; p = p->link) { + if (p->from.type == D_AUTO && p->from.node) + p->from.offset += p->from.node->stkdelta; + + if (p->to.type == D_AUTO && p->to.node) + p->to.offset += p->to.node->stkdelta; + } + + // The debug information needs accurate offsets on the symbols. + for(ll = curfn->dcl ;ll != nil; ll=ll->next) { + if (ll->n->class != PAUTO || ll->n->op != ONAME) + continue; + ll->n->xoffset += ll->n->stkdelta; + ll->n->stkdelta = 0; + } +} diff --git a/src/cmd/gc/print.c b/src/cmd/gc/print.c index e03a14080..e88e0f844 100644 --- a/src/cmd/gc/print.c +++ b/src/cmd/gc/print.c @@ -134,6 +134,10 @@ exprfmt(Fmt *f, Node *n, int prec) fmtprint(f, "(node %O)", n->op); break; + case OREGISTER: + fmtprint(f, "%R", n->val.u.reg); + break; + case OLITERAL: if(n->sym != S) { fmtprint(f, "%S", n->sym); diff --git a/src/cmd/gc/subr.c b/src/cmd/gc/subr.c index 4c0819cd8..49797f9df 100644 --- a/src/cmd/gc/subr.c +++ b/src/cmd/gc/subr.c @@ -105,7 +105,7 @@ hcrash(void) flusherrors(); if(outfile) unlink(outfile); - *(int*)0 = 0; + *(volatile int*)0 = 0; } } @@ -480,6 +480,7 @@ nod(int op, Node *nleft, Node *nright) n->right = nright; n->lineno = parserline(); n->xoffset = BADWIDTH; + n->orig = n; return n; } @@ -1031,10 +1032,21 @@ Econv(Fmt *fp) return fmtstrcpy(fp, etnames[et]); } +static const char* classnames[] = { + "Pxxx", + "PEXTERN", + "PAUTO", + "PPARAM", + "PPARAMOUT", + "PPARAMREF", + "PFUNC", +}; + int Jconv(Fmt *fp) { Node *n; + char *s; n = va_arg(fp->args, Node*); if(n->ullman != 0) @@ -1049,12 +1061,18 @@ Jconv(Fmt *fp) if(n->lineno != 0) fmtprint(fp, " l(%d)", n->lineno); - if(n->xoffset != 0) - fmtprint(fp, " x(%lld)", n->xoffset); - - if(n->class != 0) - fmtprint(fp, " class(%d)", n->class); + if(n->xoffset != BADWIDTH) + fmtprint(fp, " x(%lld%+d)", n->xoffset, n->stkdelta); + if(n->class != 0) { + s = ""; + if (n->class & PHEAP) s = ",heap"; + if ((n->class & ~PHEAP) < nelem(classnames)) + fmtprint(fp, " class(%s%s)", classnames[n->class&~PHEAP], s); + else + fmtprint(fp, " class(%d?%s)", n->class&~PHEAP, s); + } + if(n->colas != 0) fmtprint(fp, " colas(%d)", n->colas); @@ -1076,6 +1094,8 @@ Jconv(Fmt *fp) if(n->pun != 0) fmtprint(fp, " pun(%d)", n->pun); + if(n->used != 0) + fmtprint(fp, " used(%d)", n->used); return 0; } @@ -3339,6 +3359,64 @@ list(NodeList *l, Node *n) return concat(l, list1(n)); } +void +listsort(NodeList** l, int(*f)(Node*, Node*)) +{ + NodeList *l1, *l2, *le; + + if(*l == nil || (*l)->next == nil) + return; + + l1 = *l; + l2 = *l; + for(;;) { + l2 = l2->next; + if(l2 == nil) + break; + l2 = l2->next; + if(l2 == nil) + break; + l1 = l1->next; + } + + l2 = l1->next; + l1->next = nil; + l2->end = (*l)->end; + (*l)->end = l1; + + l1 = *l; + listsort(&l1, f); + listsort(&l2, f); + + if ((*f)(l1->n, l2->n) < 0) { + *l = l1; + } else { + *l = l2; + l2 = l1; + l1 = *l; + } + + // now l1 == *l; and l1 < l2 + + while ((l1 != nil) && (l2 != nil)) { + while ((l1->next != nil) && (*f)(l1->next->n, l2->n) < 0) + l1 = l1->next; + + // l1 is last one from l1 that is < l2 + le = l1->next; // le is the rest of l1, first one that is >= l2 + if (le != nil) + le->end = (*l)->end; + + (*l)->end = l1; // cut *l at l1 + *l = concat(*l, l2); // glue l2 to *l's tail + + l1 = l2; // l1 is the first element of *l that is < the new l2 + l2 = le; // ... because l2 now is the old tail of l1 + } + + *l = concat(*l, l2); // any remainder +} + NodeList* listtreecopy(NodeList *l) { diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c index 44d08352d..04dc1a507 100644 --- a/src/cmd/gc/typecheck.c +++ b/src/cmd/gc/typecheck.c @@ -8,7 +8,6 @@ * evaluates compile time constants. * marks variables that escape the local frame. * rewrites n->op to be more specific in some cases. - * sets n->walk to walking function. */ #include "go.h" @@ -33,6 +32,8 @@ static void stringtoarraylit(Node**); static Node* resolve(Node*); static Type* getforwtype(Node*); +static NodeList* typecheckdefstack; + /* * resolve ONONAME to definition, if any. */ @@ -159,7 +160,7 @@ typecheck(Node **np, int top) if(n->op == OTYPE && (ft = getforwtype(n->ntype)) != T) defertypecopy(n, ft); - walkdef(n); + typecheckdef(n); n->realtype = n->type; if(n->op == ONONAME) goto error; @@ -2523,3 +2524,294 @@ getforwtype(Node *n) } } } + +static int ntypecheckdeftype; +static NodeList *methodqueue; + +static void +domethod(Node *n) +{ + Node *nt; + + nt = n->type->nname; + typecheck(&nt, Etype); + if(nt->type == T) { + // type check failed; leave empty func + n->type->etype = TFUNC; + n->type->nod = N; + return; + } + *n->type = *nt->type; + n->type->nod = N; + checkwidth(n->type); +} + +typedef struct NodeTypeList NodeTypeList; +struct NodeTypeList { + Node *n; + Type *t; + NodeTypeList *next; +}; + +static NodeTypeList *dntq; +static NodeTypeList *dntend; + +void +defertypecopy(Node *n, Type *t) +{ + NodeTypeList *ntl; + + if(n == N || t == T) + return; + + ntl = mal(sizeof *ntl); + ntl->n = n; + ntl->t = t; + ntl->next = nil; + + if(dntq == nil) + dntq = ntl; + else + dntend->next = ntl; + + dntend = ntl; +} + +void +resumetypecopy(void) +{ + NodeTypeList *l; + + for(l=dntq; l; l=l->next) + copytype(l->n, l->t); +} + +void +copytype(Node *n, Type *t) +{ + *n->type = *t; + + t = n->type; + t->sym = n->sym; + t->local = n->local; + t->vargen = n->vargen; + t->siggen = 0; + t->method = nil; + t->nod = N; + t->printed = 0; + t->deferwidth = 0; +} + +static void +typecheckdeftype(Node *n) +{ + int maplineno, embedlineno, lno; + Type *t; + NodeList *l; + + ntypecheckdeftype++; + lno = lineno; + setlineno(n); + n->type->sym = n->sym; + n->typecheck = 1; + typecheck(&n->ntype, Etype); + if((t = n->ntype->type) == T) { + n->diag = 1; + goto ret; + } + if(n->type == T) { + n->diag = 1; + goto ret; + } + + maplineno = n->type->maplineno; + embedlineno = n->type->embedlineno; + + // copy new type and clear fields + // that don't come along. + // anything zeroed here must be zeroed in + // typedcl2 too. + copytype(n, t); + + // double-check use of type as map key. + if(maplineno) { + lineno = maplineno; + maptype(n->type, types[TBOOL]); + } + if(embedlineno) { + lineno = embedlineno; + if(isptr[t->etype]) + yyerror("embedded type cannot be a pointer"); + } + +ret: + lineno = lno; + + // if there are no type definitions going on, it's safe to + // try to resolve the method types for the interfaces + // we just read. + if(ntypecheckdeftype == 1) { + while((l = methodqueue) != nil) { + methodqueue = nil; + for(; l; l=l->next) + domethod(l->n); + } + } + ntypecheckdeftype--; +} + +void +queuemethod(Node *n) +{ + if(ntypecheckdeftype == 0) { + domethod(n); + return; + } + methodqueue = list(methodqueue, n); +} + +Node* +typecheckdef(Node *n) +{ + int lno; + Node *e; + Type *t; + NodeList *l; + + lno = lineno; + setlineno(n); + + if(n->op == ONONAME) { + if(!n->diag) { + n->diag = 1; + if(n->lineno != 0) + lineno = n->lineno; + yyerror("undefined: %S", n->sym); + } + return n; + } + + if(n->walkdef == 1) + return n; + + l = mal(sizeof *l); + l->n = n; + l->next = typecheckdefstack; + typecheckdefstack = l; + + if(n->walkdef == 2) { + flusherrors(); + print("typecheckdef loop:"); + for(l=typecheckdefstack; l; l=l->next) + print(" %S", l->n->sym); + print("\n"); + fatal("typecheckdef loop"); + } + n->walkdef = 2; + + if(n->type != T || n->sym == S) // builtin or no name + goto ret; + + switch(n->op) { + default: + fatal("typecheckdef %O", n->op); + + case OLITERAL: + if(n->ntype != N) { + typecheck(&n->ntype, Etype); + n->type = n->ntype->type; + n->ntype = N; + if(n->type == T) { + n->diag = 1; + goto ret; + } + } + e = n->defn; + n->defn = N; + if(e == N) { + lineno = n->lineno; + dump("typecheckdef nil defn", n); + yyerror("xxx"); + } + typecheck(&e, Erv | Eiota); + if(e->type != T && e->op != OLITERAL) { + yyerror("const initializer must be constant"); + goto ret; + } + if(isconst(e, CTNIL)) { + yyerror("const initializer cannot be nil"); + goto ret; + } + t = n->type; + if(t != T) { + if(!okforconst[t->etype]) { + yyerror("invalid constant type %T", t); + goto ret; + } + if(!isideal(e->type) && !eqtype(t, e->type)) { + yyerror("cannot use %+N as type %T in const initializer", e, t); + goto ret; + } + convlit(&e, t); + } + n->val = e->val; + n->type = e->type; + break; + + case ONAME: + if(n->ntype != N) { + typecheck(&n->ntype, Etype); + n->type = n->ntype->type; + if(n->type == T) { + n->diag = 1; + goto ret; + } + } + if(n->type != T) + break; + if(n->defn == N) { + if(n->etype != 0) // like OPRINTN + break; + if(nerrors > 0) { + // Can have undefined variables in x := foo + // that make x have an n->ndefn == nil. + // If there are other errors anyway, don't + // bother adding to the noise. + break; + } + fatal("var without type, init: %S", n->sym); + } + if(n->defn->op == ONAME) { + typecheck(&n->defn, Erv); + n->type = n->defn->type; + break; + } + typecheck(&n->defn, Etop); // fills in n->type + break; + + case OTYPE: + if(curfn) + defercheckwidth(); + n->walkdef = 1; + n->type = typ(TFORW); + n->type->sym = n->sym; + typecheckdeftype(n); + if(curfn) + resumecheckwidth(); + break; + + case OPACK: + // nothing to see here + break; + } + +ret: + if(typecheckdefstack->n != n) + fatal("typecheckdefstack mismatch"); + l = typecheckdefstack; + typecheckdefstack = l->next; + + lineno = lno; + n->walkdef = 1; + return n; +} diff --git a/src/cmd/gc/walk.c b/src/cmd/gc/walk.c index ccc65ff21..65a504bff 100644 --- a/src/cmd/gc/walk.c +++ b/src/cmd/gc/walk.c @@ -21,8 +21,6 @@ static Node* addstr(Node*, NodeList**); static Node* appendslice(Node*, NodeList**); static Node* append(Node*, NodeList**); -static NodeList* walkdefstack; - // can this code branch reach the end // without an unconditional RETURN // this is hard, so it is conservative @@ -100,296 +98,6 @@ walk(Node *fn) } } -static int nwalkdeftype; -static NodeList *methodqueue; - -static void -domethod(Node *n) -{ - Node *nt; - - nt = n->type->nname; - typecheck(&nt, Etype); - if(nt->type == T) { - // type check failed; leave empty func - n->type->etype = TFUNC; - n->type->nod = N; - return; - } - *n->type = *nt->type; - n->type->nod = N; - checkwidth(n->type); -} - -typedef struct NodeTypeList NodeTypeList; -struct NodeTypeList { - Node *n; - Type *t; - NodeTypeList *next; -}; - -static NodeTypeList *dntq; -static NodeTypeList *dntend; - -void -defertypecopy(Node *n, Type *t) -{ - NodeTypeList *ntl; - - if(n == N || t == T) - return; - - ntl = mal(sizeof *ntl); - ntl->n = n; - ntl->t = t; - ntl->next = nil; - - if(dntq == nil) - dntq = ntl; - else - dntend->next = ntl; - - dntend = ntl; -} - -void -resumetypecopy(void) -{ - NodeTypeList *l; - - for(l=dntq; l; l=l->next) - copytype(l->n, l->t); -} - -void -copytype(Node *n, Type *t) -{ - *n->type = *t; - - t = n->type; - t->sym = n->sym; - t->local = n->local; - t->vargen = n->vargen; - t->siggen = 0; - t->method = nil; - t->nod = N; - t->printed = 0; - t->deferwidth = 0; -} - -static void -walkdeftype(Node *n) -{ - int maplineno, embedlineno, lno; - Type *t; - NodeList *l; - - nwalkdeftype++; - lno = lineno; - setlineno(n); - n->type->sym = n->sym; - n->typecheck = 1; - typecheck(&n->ntype, Etype); - if((t = n->ntype->type) == T) { - n->diag = 1; - goto ret; - } - if(n->type == T) { - n->diag = 1; - goto ret; - } - - maplineno = n->type->maplineno; - embedlineno = n->type->embedlineno; - - // copy new type and clear fields - // that don't come along. - // anything zeroed here must be zeroed in - // typedcl2 too. - copytype(n, t); - - // double-check use of type as map key. - if(maplineno) { - lineno = maplineno; - maptype(n->type, types[TBOOL]); - } - if(embedlineno) { - lineno = embedlineno; - if(isptr[t->etype]) - yyerror("embedded type cannot be a pointer"); - } - -ret: - lineno = lno; - - // if there are no type definitions going on, it's safe to - // try to resolve the method types for the interfaces - // we just read. - if(nwalkdeftype == 1) { - while((l = methodqueue) != nil) { - methodqueue = nil; - for(; l; l=l->next) - domethod(l->n); - } - } - nwalkdeftype--; -} - -void -queuemethod(Node *n) -{ - if(nwalkdeftype == 0) { - domethod(n); - return; - } - methodqueue = list(methodqueue, n); -} - -Node* -walkdef(Node *n) -{ - int lno; - Node *e; - Type *t; - NodeList *l; - - lno = lineno; - setlineno(n); - - if(n->op == ONONAME) { - if(!n->diag) { - n->diag = 1; - if(n->lineno != 0) - lineno = n->lineno; - yyerror("undefined: %S", n->sym); - } - return n; - } - - if(n->walkdef == 1) - return n; - - l = mal(sizeof *l); - l->n = n; - l->next = walkdefstack; - walkdefstack = l; - - if(n->walkdef == 2) { - flusherrors(); - print("walkdef loop:"); - for(l=walkdefstack; l; l=l->next) - print(" %S", l->n->sym); - print("\n"); - fatal("walkdef loop"); - } - n->walkdef = 2; - - if(n->type != T || n->sym == S) // builtin or no name - goto ret; - - switch(n->op) { - default: - fatal("walkdef %O", n->op); - - case OLITERAL: - if(n->ntype != N) { - typecheck(&n->ntype, Etype); - n->type = n->ntype->type; - n->ntype = N; - if(n->type == T) { - n->diag = 1; - goto ret; - } - } - e = n->defn; - n->defn = N; - if(e == N) { - lineno = n->lineno; - dump("walkdef nil defn", n); - yyerror("xxx"); - } - typecheck(&e, Erv | Eiota); - if(e->type != T && e->op != OLITERAL) { - yyerror("const initializer must be constant"); - goto ret; - } - if(isconst(e, CTNIL)) { - yyerror("const initializer cannot be nil"); - goto ret; - } - t = n->type; - if(t != T) { - if(!okforconst[t->etype]) { - yyerror("invalid constant type %T", t); - goto ret; - } - if(!isideal(e->type) && !eqtype(t, e->type)) { - yyerror("cannot use %+N as type %T in const initializer", e, t); - goto ret; - } - convlit(&e, t); - } - n->val = e->val; - n->type = e->type; - break; - - case ONAME: - if(n->ntype != N) { - typecheck(&n->ntype, Etype); - n->type = n->ntype->type; - if(n->type == T) { - n->diag = 1; - goto ret; - } - } - if(n->type != T) - break; - if(n->defn == N) { - if(n->etype != 0) // like OPRINTN - break; - if(nerrors > 0) { - // Can have undefined variables in x := foo - // that make x have an n->ndefn == nil. - // If there are other errors anyway, don't - // bother adding to the noise. - break; - } - fatal("var without type, init: %S", n->sym); - } - if(n->defn->op == ONAME) { - typecheck(&n->defn, Erv); - n->type = n->defn->type; - break; - } - typecheck(&n->defn, Etop); // fills in n->type - break; - - case OTYPE: - if(curfn) - defercheckwidth(); - n->walkdef = 1; - n->type = typ(TFORW); - n->type->sym = n->sym; - walkdeftype(n); - if(curfn) - resumecheckwidth(); - break; - - case OPACK: - // nothing to see here - break; - } - -ret: - if(walkdefstack->n != n) - fatal("walkdefstack mismatch"); - l = walkdefstack; - walkdefstack = l->next; - - lineno = lno; - n->walkdef = 1; - return n; -} void walkstmtlist(NodeList *l) diff --git a/src/cmd/godefs/a.h b/src/cmd/godefs/a.h index 03ab91f65..9b4957467 100644 --- a/src/cmd/godefs/a.h +++ b/src/cmd/godefs/a.h @@ -75,6 +75,7 @@ extern Const *con; extern int ncon; extern Type **typ; extern int ntyp; +extern int kindsize[]; // Language output typedef struct Lang Lang; diff --git a/src/cmd/godefs/main.c b/src/cmd/godefs/main.c index d4163421d..6a8630179 100644 --- a/src/cmd/godefs/main.c +++ b/src/cmd/godefs/main.c @@ -181,7 +181,7 @@ main(int argc, char **argv) char **av, *q, *r, *tofree, *name; char nambuf[100]; Biobuf *bin, *bout; - Type *t; + Type *t, *tt; Field *f; int orig_output_fd; @@ -373,8 +373,16 @@ Continue: prefix = prefixlen(t); for(j=0; j<t->nf; j++) { f = &t->f[j]; - if(f->type->kind == 0) - continue; + if(f->type->kind == 0 && f->size <= 64 && (f->size&(f->size-1)) == 0) { + // unknown type but <= 64 bits and bit size is a power of two. + // could be enum - make Uint64 and then let it reduce + tt = emalloc(sizeof *tt); + *tt = *f->type; + f->type = tt; + tt->kind = Uint64; + while(tt->kind > Uint8 && kindsize[tt->kind] > f->size) + tt->kind -= 2; + } // padding if(t->kind == Struct || lang == &go) { if(f->offset%8 != 0 || f->size%8 != 0) { diff --git a/src/cmd/godefs/stabs.c b/src/cmd/godefs/stabs.c index 30a05fc70..2c3d431b8 100644 --- a/src/cmd/godefs/stabs.c +++ b/src/cmd/godefs/stabs.c @@ -149,7 +149,7 @@ Intrange intranges[] = { 16, 0, Void, }; -static int kindsize[] = { +int kindsize[] = { 0, 0, 8, @@ -381,14 +381,6 @@ parsedef(char **pp, char *name) while(f->type->kind == Typedef) f->type = f->type->type; - if(f->type->kind == 0 && f->size <= 64 && (f->size&(f->size-1)) == 0) { - // unknown type but <= 64 bits and bit size is a power of two. - // could be enum - make Uint64 and then let it reduce - tt = emalloc(sizeof *tt); - *tt = *f->type; - f->type = tt; - tt->kind = Uint64; - } // rewrite // uint32 x : 8; diff --git a/src/cmd/godoc/spec.go b/src/cmd/godoc/spec.go index d863ca0d8..444e36e08 100644 --- a/src/cmd/godoc/spec.go +++ b/src/cmd/godoc/spec.go @@ -129,6 +129,9 @@ func (p *ebnfParser) parseTerm() bool { func (p *ebnfParser) parseSequence() { + if !p.parseTerm() { + p.errorExpected(p.pos, "term") + } for p.parseTerm() { } } @@ -148,7 +151,9 @@ func (p *ebnfParser) parseExpression() { func (p *ebnfParser) parseProduction() { p.parseIdentifier(true) p.expect(token.ASSIGN) - p.parseExpression() + if p.tok != token.PERIOD { + p.parseExpression() + } p.expect(token.PERIOD) } diff --git a/src/cmd/gofix/main.go b/src/cmd/gofix/main.go index ba2061a00..1b091c18a 100644 --- a/src/cmd/gofix/main.go +++ b/src/cmd/gofix/main.go @@ -248,5 +248,11 @@ func diff(b1, b2 []byte) (data []byte, err os.Error) { f1.Write(b1) f2.Write(b2) - return exec.Command("diff", f1.Name(), f2.Name()).CombinedOutput() + data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput() + if len(data) > 0 { + // diff exits with a non-zero status when the files don't match. + // Ignore that failure as long as we get output. + err = nil + } + return } diff --git a/src/cmd/gofmt/gofmt.go b/src/cmd/gofmt/gofmt.go index 16bcd3c4d..ea1c1b00f 100644 --- a/src/cmd/gofmt/gofmt.go +++ b/src/cmd/gofmt/gofmt.go @@ -260,5 +260,12 @@ func diff(b1, b2 []byte) (data []byte, err os.Error) { f1.Write(b1) f2.Write(b2) - return exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput() + data, err = exec.Command("diff", "-u", f1.Name(), f2.Name()).CombinedOutput() + if len(data) > 0 { + // diff exits with a non-zero status when the files don't match. + // Ignore that failure as long as we get output. + err = nil + } + return + } diff --git a/src/cmd/gofmt/test.sh b/src/cmd/gofmt/test.sh index f60ff9b32..5dce2ed7a 100755 --- a/src/cmd/gofmt/test.sh +++ b/src/cmd/gofmt/test.sh @@ -36,7 +36,7 @@ apply1() { # the following files are skipped because they are test cases # for syntax errors and thus won't parse in the first place: case `basename "$F"` in - func3.go | const2.go | char_lit1.go | blank1.go | \ + func3.go | const2.go | char_lit1.go | blank1.go | ddd1.go | \ bug014.go | bug050.go | bug068.go | bug083.go | bug088.go | \ bug106.go | bug121.go | bug125.go | bug133.go | bug160.go | \ bug163.go | bug166.go | bug169.go | bug217.go | bug222.go | \ diff --git a/src/cmd/gopack/ar.c b/src/cmd/gopack/ar.c index 017978ced..0b5e608c7 100644 --- a/src/cmd/gopack/ar.c +++ b/src/cmd/gopack/ar.c @@ -1532,25 +1532,7 @@ arwrite(int fd, Armember *bp) int page(Arfile *ap) { - Armember *bp; - - bp = ap->head; - if (!ap->paged) { /* not yet paged - create file */ - ap->fname = mktemp(ap->fname); - ap->fd = create(ap->fname, ORDWR|ORCLOSE, 0600); - if (ap->fd < 0) { - fprint(2,"gopack: can't create temp file\n"); - return 0; - } - ap->paged = 1; - } - if (!arwrite(ap->fd, bp)) /* write member and free buffer block */ - return 0; - ap->head = bp->next; - if (ap->tail == bp) - ap->tail = bp->next; - free(bp->member); - free(bp); + sysfatal("page"); return 1; } diff --git a/src/cmd/ld/dwarf.c b/src/cmd/ld/dwarf.c index de600f555..1721def67 100644 --- a/src/cmd/ld/dwarf.c +++ b/src/cmd/ld/dwarf.c @@ -1348,7 +1348,7 @@ synthesizemaptypes(DWDie *die) valtype = defptrto(valtype); newrefattr(fld, DW_AT_type, valtype); newmemberoffsetattr(fld, hashsize + datavo); - newattr(dwhe, DW_AT_byte_size, DW_CLS_CONSTANT, hashsize + datsize, NULL); + newattr(dwhe, DW_AT_byte_size, DW_CLS_CONSTANT, hashsize + datsize, nil); // Construct hash_subtable<hash_entry<K,V>> dwhs = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, @@ -1359,7 +1359,7 @@ synthesizemaptypes(DWDie *die) substitutetype(dwhs, "end", defptrto(dwhe)); substitutetype(dwhs, "entry", dwhe); // todo: []hash_entry with dynamic size newattr(dwhs, DW_AT_byte_size, DW_CLS_CONSTANT, - getattr(hash_subtable, DW_AT_byte_size)->value, NULL); + getattr(hash_subtable, DW_AT_byte_size)->value, nil); // Construct hash<K,V> dwh = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, @@ -1369,7 +1369,7 @@ synthesizemaptypes(DWDie *die) copychildren(dwh, hash); substitutetype(dwh, "st", defptrto(dwhs)); newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT, - getattr(hash, DW_AT_byte_size)->value, NULL); + getattr(hash, DW_AT_byte_size)->value, nil); newrefattr(die, DW_AT_type, defptrto(dwh)); } @@ -1401,30 +1401,30 @@ synthesizechantypes(DWDie *die) // sudog<T> dws = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, mkinternaltypename("sudog", - getattr(elemtype, DW_AT_name)->data, NULL)); + getattr(elemtype, DW_AT_name)->data, nil)); copychildren(dws, sudog); substitutetype(dws, "elem", elemtype); newattr(dws, DW_AT_byte_size, DW_CLS_CONSTANT, - sudogsize + (elemsize > 8 ? elemsize - 8 : 0), NULL); + sudogsize + (elemsize > 8 ? elemsize - 8 : 0), nil); // waitq<T> dww = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, - mkinternaltypename("waitq", getattr(elemtype, DW_AT_name)->data, NULL)); + mkinternaltypename("waitq", getattr(elemtype, DW_AT_name)->data, nil)); copychildren(dww, waitq); substitutetype(dww, "first", defptrto(dws)); substitutetype(dww, "last", defptrto(dws)); newattr(dww, DW_AT_byte_size, DW_CLS_CONSTANT, - getattr(waitq, DW_AT_byte_size)->value, NULL); + getattr(waitq, DW_AT_byte_size)->value, nil); // hchan<T> dwh = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, - mkinternaltypename("hchan", getattr(elemtype, DW_AT_name)->data, NULL)); + mkinternaltypename("hchan", getattr(elemtype, DW_AT_name)->data, nil)); copychildren(dwh, hchan); substitutetype(dwh, "recvq", dww); substitutetype(dwh, "sendq", dww); substitutetype(dwh, "free", defptrto(dws)); newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT, - getattr(hchan, DW_AT_byte_size)->value, NULL); + getattr(hchan, DW_AT_byte_size)->value, nil); newrefattr(die, DW_AT_type, defptrto(dwh)); } @@ -1436,6 +1436,7 @@ defdwsymb(Sym* sym, char *s, int t, vlong v, vlong size, int ver, Sym *gotype) { DWDie *dv, *dt; + USED(size); if (strncmp(s, "go.string.", 10) == 0) return; @@ -1592,7 +1593,7 @@ addhistfile(char *zentry) // if the histfile stack contains ..../runtime/runtime_defs.go // use that to set gdbscript static void -finddebugruntimepath() +finddebugruntimepath(void) { int i, l; char *c; @@ -1841,7 +1842,8 @@ writelines(void) char *n, *nn; unitstart = -1; - epc = pc = 0; + pc = 0; + epc = 0; lc = 1; llc = 1; currfile = -1; @@ -1903,7 +1905,8 @@ writelines(void) // 4 zeros: the string termination + 3 fields. } - epc = pc = s->text->pc; + pc = s->text->pc; + epc = pc; currfile = 1; lc = 1; llc = 1; @@ -1992,7 +1995,7 @@ writelines(void) newrefattr(dwvar, DW_AT_type, defgotype(a->gotype)); // push dwvar down dwfunc->child to preserve order - newattr(dwvar, DW_AT_internal_location, DW_CLS_CONSTANT, offs, NULL); + newattr(dwvar, DW_AT_internal_location, DW_CLS_CONSTANT, offs, nil); dwfunc->child = dwvar->link; // take dwvar out from the top of the list for (dws = &dwfunc->child; *dws != nil; dws = &(*dws)->link) if (offs > getattr(*dws, DW_AT_internal_location)->value) @@ -2345,7 +2348,11 @@ dwarfemitdebugsections(void) infoo = cpos(); writeinfo(); - gdbscripto = arangeso = pubtypeso = pubnameso = infoe = cpos(); + infoe = cpos(); + pubnameso = infoe; + pubtypeso = infoe; + arangeso = infoe; + gdbscripto = infoe; if (fwdcount > 0) { if (debug['v']) diff --git a/src/cmd/ld/go.c b/src/cmd/ld/go.c index a19fe460d..05d1cc136 100644 --- a/src/cmd/ld/go.c +++ b/src/cmd/ld/go.c @@ -415,8 +415,8 @@ loaddynimport(char *file, char *pkg, char *p, int n) char *pend, *next, *name, *def, *p0, *lib, *q; Sym *s; + USED(file); pend = p + n; - p0 = p; for(; p<pend; p=next) { next = strchr(p, '\n'); if(next == nil) @@ -485,8 +485,8 @@ loaddynexport(char *file, char *pkg, char *p, int n) char *pend, *next, *local, *elocal, *remote, *p0; Sym *s; + USED(file); pend = p + n; - p0 = p; for(; p<pend; p=next) { next = strchr(p, '\n'); if(next == nil) diff --git a/src/cmd/ld/lib.c b/src/cmd/ld/lib.c index 105d982e4..04ee790a4 100644 --- a/src/cmd/ld/lib.c +++ b/src/cmd/ld/lib.c @@ -69,7 +69,7 @@ libinit(void) // add goroot to the end of the libdir list. libdir[nlibdir++] = smprint("%s/pkg/%s_%s", goroot, goos, goarch); - unlink(outfile); + remove(outfile); cout = create(outfile, 1, 0775); if(cout < 0) { diag("cannot create %s", outfile); @@ -235,24 +235,34 @@ addlibpath(char *srcref, char *objref, char *file, char *pkg) } void -loadlib(void) +loadinternal(char *name) { char pname[1024]; int i, found; found = 0; for(i=0; i<nlibdir; i++) { - snprint(pname, sizeof pname, "%s/runtime.a", libdir[i]); + snprint(pname, sizeof pname, "%s/%s.a", libdir[i], name); if(debug['v']) - Bprint(&bso, "searching for runtime.a in %s\n", pname); + Bprint(&bso, "searching for %s.a in %s\n", name, pname); if(access(pname, AEXIST) >= 0) { - addlibpath("internal", "internal", pname, "runtime"); + addlibpath("internal", "internal", pname, name); found = 1; break; } } if(!found) - Bprint(&bso, "warning: unable to find runtime.a\n"); + Bprint(&bso, "warning: unable to find %s.a\n", name); +} + +void +loadlib(void) +{ + int i; + + loadinternal("runtime"); + if(thechar == '5') + loadinternal("math"); for(i=0; i<libraryp; i++) { if(debug['v']) @@ -398,9 +408,6 @@ ldobj(Biobuf *f, char *pkg, int64 len, char *pn, int whence) eof = Boffset(f) + len; pn = strdup(pn); - - USED(c4); - USED(magic); c1 = Bgetc(f); c2 = Bgetc(f); @@ -410,7 +417,7 @@ ldobj(Biobuf *f, char *pkg, int64 len, char *pn, int whence) Bungetc(f); Bungetc(f); Bungetc(f); - + magic = c1<<24 | c2<<16 | c3<<8 | c4; if(magic == 0x7f454c46) { // \x7F E L F ldelf(f, pkg, len, pn); @@ -498,7 +505,6 @@ _lookup(char *symb, int v, int creat) // not if(h < 0) h = ~h, because gcc 4.3 -O2 miscompiles it. h &= 0xffffff; h %= NHASH; - c = symb[0]; for(s = hash[h]; s != S; s = s->hash) if(memcmp(s->name, symb, l) == 0) return s; @@ -523,7 +529,7 @@ _lookup(char *symb, int v, int creat) s->size = 0; hash[h] = s; nsymbol++; - + s->allsym = allsym; allsym = s; return s; @@ -550,7 +556,6 @@ copyhistfrog(char *buf, int nbuf) p = buf; ep = buf + nbuf; - i = 0; for(i=0; i<histfrogp; i++) { p = seprint(p, ep, "%s", histfrog[i]->name+1); if(i+1<histfrogp && (p == buf || p[-1] != '/')) diff --git a/src/cmd/ld/lib.h b/src/cmd/ld/lib.h index f69f5a35d..dfd18fbff 100644 --- a/src/cmd/ld/lib.h +++ b/src/cmd/ld/lib.h @@ -186,8 +186,7 @@ vlong addsize(Sym*, Sym*); vlong adduint8(Sym*, uint8); vlong adduint16(Sym*, uint16); void asmsym(void); -void asmelfsym32(void); -void asmelfsym64(void); +void asmelfsym(void); void asmplan9sym(void); void strnput(char*, int); void dodata(void); diff --git a/src/cmd/ld/symtab.c b/src/cmd/ld/symtab.c index da698fcc0..e3093b2aa 100644 --- a/src/cmd/ld/symtab.c +++ b/src/cmd/ld/symtab.c @@ -61,49 +61,35 @@ putelfstr(char *s) } void -putelfsym64(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go) +putelfsyment(int off, vlong addr, vlong size, int info, int shndx) { - int bind, type, shndx, stroff; - - bind = STB_GLOBAL; - switch(t) { - default: - return; - case 'T': - type = STT_FUNC; - shndx = elftextsh + 0; - break; - case 'D': - type = STT_OBJECT; - shndx = elftextsh + 1; + switch(thechar) { + case '6': + LPUT(off); + cput(info); + cput(0); + WPUT(shndx); + VPUT(addr); + VPUT(size); + symsize += ELF64SYMSIZE; break; - case 'B': - type = STT_OBJECT; - shndx = elftextsh + 2; + default: + LPUT(off); + LPUT(addr); + LPUT(size); + cput(info); + cput(0); + WPUT(shndx); + symsize += ELF32SYMSIZE; break; } - - stroff = putelfstr(s); - LPUT(stroff); // string - cput((bind<<4)|(type&0xF)); - cput(0); - WPUT(shndx); - VPUT(addr); - VPUT(size); } void -asmelfsym64(void) +putelfsym(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go) { - genasmsym(putelfsym64); -} + int bind, type, shndx, off; -void -putelfsym32(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go) -{ - int bind, type, shndx, stroff; - - bind = STB_GLOBAL; switch(t) { default: return; @@ -113,27 +99,27 @@ putelfsym32(Sym *x, char *s, int t, vlong addr, vlong size, int ver, Sym *go) break; case 'D': type = STT_OBJECT; - shndx = elftextsh + 1; + if((x->type&~SSUB) == SRODATA) + shndx = elftextsh + 1; + else + shndx = elftextsh + 2; break; case 'B': type = STT_OBJECT; - shndx = elftextsh + 2; + shndx = elftextsh + 3; break; } - - stroff = putelfstr(s); - LPUT(stroff); // string - LPUT(addr); - LPUT(size); - cput((bind<<4)|(type&0xF)); - cput(0); - WPUT(shndx); + bind = ver ? STB_LOCAL : STB_GLOBAL; + off = putelfstr(s); + putelfsyment(off, addr, size, (bind<<4)|(type&0xf), shndx); } void -asmelfsym32(void) +asmelfsym(void) { - genasmsym(putelfsym32); + // the first symbol entry is reserved + putelfsyment(0, 0, 0, (STB_LOCAL<<4)|STT_NOTYPE, 0); + genasmsym(putelfsym); } void diff --git a/src/libmach/8db.c b/src/libmach/8db.c index 80aa4fe69..5a195baf8 100644 --- a/src/libmach/8db.c +++ b/src/libmach/8db.c @@ -62,32 +62,32 @@ static char LESSSTACK[] = "sys·lessstack"; static char MORESTACK[] = "sys·morestack"; static char *excname[] = { -[0] "divide error", -[1] "debug exception", -[4] "overflow", -[5] "bounds check", -[6] "invalid opcode", -[7] "math coprocessor emulation", -[8] "double fault", -[9] "math coprocessor overrun", -[10] "invalid TSS", -[11] "segment not present", -[12] "stack exception", -[13] "general protection violation", -[14] "page fault", -[16] "math coprocessor error", -[17] "alignment check", -[18] "machine check", -[19] "floating-point exception", -[24] "clock", -[25] "keyboard", -[27] "modem status", -[28] "serial line status", -[30] "floppy disk", -[36] "mouse", -[37] "math coprocessor", -[38] "hard disk", -[64] "system call", +[0] = "divide error", +[1] = "debug exception", +[4] = "overflow", +[5] = "bounds check", +[6] = "invalid opcode", +[7] = "math coprocessor emulation", +[8] = "double fault", +[9] = "math coprocessor overrun", +[10] = "invalid TSS", +[11] = "segment not present", +[12] = "stack exception", +[13] = "general protection violation", +[14] = "page fault", +[16] = "math coprocessor error", +[17] = "alignment check", +[18] = "machine check", +[19] = "floating-point exception", +[24] = "clock", +[25] = "keyboard", +[27] = "modem status", +[28] = "serial line status", +[30] = "floppy disk", +[36] = "mouse", +[37] = "math coprocessor", +[38] = "hard disk", +[64] = "system call", }; Machdata i386mach = @@ -436,28 +436,28 @@ enum { static Optable optab0F00[8]= { -[0x00] 0,0, "MOVW LDT,%e", -[0x01] 0,0, "MOVW TR,%e", -[0x02] 0,0, "MOVW %e,LDT", -[0x03] 0,0, "MOVW %e,TR", -[0x04] 0,0, "VERR %e", -[0x05] 0,0, "VERW %e", +[0x00] = { 0,0, "MOVW LDT,%e" }, +[0x01] = { 0,0, "MOVW TR,%e" }, +[0x02] = { 0,0, "MOVW %e,LDT" }, +[0x03] = { 0,0, "MOVW %e,TR" }, +[0x04] = { 0,0, "VERR %e" }, +[0x05] = { 0,0, "VERW %e" }, }; static Optable optab0F01[8]= { -[0x00] 0,0, "MOVL GDTR,%e", -[0x01] 0,0, "MOVL IDTR,%e", -[0x02] 0,0, "MOVL %e,GDTR", -[0x03] 0,0, "MOVL %e,IDTR", -[0x04] 0,0, "MOVW MSW,%e", /* word */ -[0x06] 0,0, "MOVW %e,MSW", /* word */ -[0x07] 0,0, "INVLPG %e", /* or SWAPGS */ +[0x00] = { 0,0, "MOVL GDTR,%e" }, +[0x01] = { 0,0, "MOVL IDTR,%e" }, +[0x02] = { 0,0, "MOVL %e,GDTR" }, +[0x03] = { 0,0, "MOVL %e,IDTR" }, +[0x04] = { 0,0, "MOVW MSW,%e" }, /* word */ +[0x06] = { 0,0, "MOVW %e,MSW" }, /* word */ +[0x07] = { 0,0, "INVLPG %e" }, /* or SWAPGS */ }; static Optable optab0F01F8[1]= { -[0x00] 0,0, "SWAPGS", +[0x00] = { 0,0, "SWAPGS" }, }; /* 0F71 */ @@ -466,13 +466,13 @@ static Optable optab0F01F8[1]= static Optable optab0FAE[8]= { -[0x00] 0,0, "FXSAVE %e", -[0x01] 0,0, "FXRSTOR %e", -[0x02] 0,0, "LDMXCSR %e", -[0x03] 0,0, "STMXCSR %e", -[0x05] 0,0, "LFENCE", -[0x06] 0,0, "MFENCE", -[0x07] 0,0, "SFENCE", +[0x00] = { 0,0, "FXSAVE %e" }, +[0x01] = { 0,0, "FXRSTOR %e" }, +[0x02] = { 0,0, "LDMXCSR %e" }, +[0x03] = { 0,0, "STMXCSR %e" }, +[0x05] = { 0,0, "LFENCE" }, +[0x06] = { 0,0, "MFENCE" }, +[0x07] = { 0,0, "SFENCE" }, }; /* 0F18 */ @@ -480,455 +480,455 @@ static Optable optab0FAE[8]= static Optable optab0FBA[8]= { -[0x04] Ib,0, "BT%S %i,%e", -[0x05] Ib,0, "BTS%S %i,%e", -[0x06] Ib,0, "BTR%S %i,%e", -[0x07] Ib,0, "BTC%S %i,%e", +[0x04] = { Ib,0, "BT%S %i,%e" }, +[0x05] = { Ib,0, "BTS%S %i,%e" }, +[0x06] = { Ib,0, "BTR%S %i,%e" }, +[0x07] = { Ib,0, "BTC%S %i,%e" }, }; static Optable optab0F0F[256]= { -[0x0c] 0,0, "PI2FW %m,%M", -[0x0d] 0,0, "PI2L %m,%M", -[0x1c] 0,0, "PF2IW %m,%M", -[0x1d] 0,0, "PF2IL %m,%M", -[0x8a] 0,0, "PFNACC %m,%M", -[0x8e] 0,0, "PFPNACC %m,%M", -[0x90] 0,0, "PFCMPGE %m,%M", -[0x94] 0,0, "PFMIN %m,%M", -[0x96] 0,0, "PFRCP %m,%M", -[0x97] 0,0, "PFRSQRT %m,%M", -[0x9a] 0,0, "PFSUB %m,%M", -[0x9e] 0,0, "PFADD %m,%M", -[0xa0] 0,0, "PFCMPGT %m,%M", -[0xa4] 0,0, "PFMAX %m,%M", -[0xa6] 0,0, "PFRCPIT1 %m,%M", -[0xa7] 0,0, "PFRSQIT1 %m,%M", -[0xaa] 0,0, "PFSUBR %m,%M", -[0xae] 0,0, "PFACC %m,%M", -[0xb0] 0,0, "PFCMPEQ %m,%M", -[0xb4] 0,0, "PFMUL %m,%M", -[0xb6] 0,0, "PFRCPI2T %m,%M", -[0xb7] 0,0, "PMULHRW %m,%M", -[0xbb] 0,0, "PSWAPL %m,%M", +[0x0c] = { 0,0, "PI2FW %m,%M" }, +[0x0d] = { 0,0, "PI2L %m,%M" }, +[0x1c] = { 0,0, "PF2IW %m,%M" }, +[0x1d] = { 0,0, "PF2IL %m,%M" }, +[0x8a] = { 0,0, "PFNACC %m,%M" }, +[0x8e] = { 0,0, "PFPNACC %m,%M" }, +[0x90] = { 0,0, "PFCMPGE %m,%M" }, +[0x94] = { 0,0, "PFMIN %m,%M" }, +[0x96] = { 0,0, "PFRCP %m,%M" }, +[0x97] = { 0,0, "PFRSQRT %m,%M" }, +[0x9a] = { 0,0, "PFSUB %m,%M" }, +[0x9e] = { 0,0, "PFADD %m,%M" }, +[0xa0] = { 0,0, "PFCMPGT %m,%M" }, +[0xa4] = { 0,0, "PFMAX %m,%M" }, +[0xa6] = { 0,0, "PFRCPIT1 %m,%M" }, +[0xa7] = { 0,0, "PFRSQIT1 %m,%M" }, +[0xaa] = { 0,0, "PFSUBR %m,%M" }, +[0xae] = { 0,0, "PFACC %m,%M" }, +[0xb0] = { 0,0, "PFCMPEQ %m,%M" }, +[0xb4] = { 0,0, "PFMUL %m,%M" }, +[0xb6] = { 0,0, "PFRCPI2T %m,%M" }, +[0xb7] = { 0,0, "PMULHRW %m,%M" }, +[0xbb] = { 0,0, "PSWAPL %m,%M" }, }; static Optable optab0FC7[8]= { -[0x01] 0,0, "CMPXCHG8B %e", +[0x01] = { 0,0, "CMPXCHG8B %e" }, }; static Optable optab660F71[8]= { -[0x02] Ib,0, "PSRLW %i,%X", -[0x04] Ib,0, "PSRAW %i,%X", -[0x06] Ib,0, "PSLLW %i,%X", +[0x02] = { Ib,0, "PSRLW %i,%X" }, +[0x04] = { Ib,0, "PSRAW %i,%X" }, +[0x06] = { Ib,0, "PSLLW %i,%X" }, }; static Optable optab660F72[8]= { -[0x02] Ib,0, "PSRLL %i,%X", -[0x04] Ib,0, "PSRAL %i,%X", -[0x06] Ib,0, "PSLLL %i,%X", +[0x02] = { Ib,0, "PSRLL %i,%X" }, +[0x04] = { Ib,0, "PSRAL %i,%X" }, +[0x06] = { Ib,0, "PSLLL %i,%X" }, }; static Optable optab660F73[8]= { -[0x02] Ib,0, "PSRLQ %i,%X", -[0x03] Ib,0, "PSRLO %i,%X", -[0x06] Ib,0, "PSLLQ %i,%X", -[0x07] Ib,0, "PSLLO %i,%X", +[0x02] = { Ib,0, "PSRLQ %i,%X" }, +[0x03] = { Ib,0, "PSRLO %i,%X" }, +[0x06] = { Ib,0, "PSLLQ %i,%X" }, +[0x07] = { Ib,0, "PSLLO %i,%X" }, }; static Optable optab660F[256]= { -[0x2B] RM,0, "MOVNTPD %x,%e", -[0x2E] RM,0, "UCOMISD %x,%X", -[0x2F] RM,0, "COMISD %x,%X", -[0x5A] RM,0, "CVTPD2PS %x,%X", -[0x5B] RM,0, "CVTPS2PL %x,%X", -[0x6A] RM,0, "PUNPCKHLQ %x,%X", -[0x6B] RM,0, "PACKSSLW %x,%X", -[0x6C] RM,0, "PUNPCKLQDQ %x,%X", -[0x6D] RM,0, "PUNPCKHQDQ %x,%X", -[0x6E] RM,0, "MOV%S %e,%X", -[0x6F] RM,0, "MOVO %x,%X", /* MOVDQA */ -[0x70] RM,Ib, "PSHUFL %i,%x,%X", -[0x71] RMOP,0, optab660F71, -[0x72] RMOP,0, optab660F72, -[0x73] RMOP,0, optab660F73, -[0x7E] RM,0, "MOV%S %X,%e", -[0x7F] RM,0, "MOVO %X,%x", -[0xC4] RM,Ib, "PINSRW %i,%e,%X", -[0xC5] RMR,Ib, "PEXTRW %i,%X,%e", -[0xD4] RM,0, "PADDQ %x,%X", -[0xD5] RM,0, "PMULLW %x,%X", -[0xD6] RM,0, "MOVQ %X,%x", -[0xE6] RM,0, "CVTTPD2PL %x,%X", -[0xE7] RM,0, "MOVNTO %X,%e", -[0xF7] RM,0, "MASKMOVOU %x,%X", +[0x2B] = { RM,0, "MOVNTPD %x,%e" }, +[0x2E] = { RM,0, "UCOMISD %x,%X" }, +[0x2F] = { RM,0, "COMISD %x,%X" }, +[0x5A] = { RM,0, "CVTPD2PS %x,%X" }, +[0x5B] = { RM,0, "CVTPS2PL %x,%X" }, +[0x6A] = { RM,0, "PUNPCKHLQ %x,%X" }, +[0x6B] = { RM,0, "PACKSSLW %x,%X" }, +[0x6C] = { RM,0, "PUNPCKLQDQ %x,%X" }, +[0x6D] = { RM,0, "PUNPCKHQDQ %x,%X" }, +[0x6E] = { RM,0, "MOV%S %e,%X" }, +[0x6F] = { RM,0, "MOVO %x,%X" }, /* MOVDQA */ +[0x70] = { RM,Ib, "PSHUFL %i,%x,%X" }, +[0x71] = { RMOP,0, optab660F71 }, +[0x72] = { RMOP,0, optab660F72 }, +[0x73] = { RMOP,0, optab660F73 }, +[0x7E] = { RM,0, "MOV%S %X,%e" }, +[0x7F] = { RM,0, "MOVO %X,%x" }, +[0xC4] = { RM,Ib, "PINSRW %i,%e,%X" }, +[0xC5] = { RMR,Ib, "PEXTRW %i,%X,%e" }, +[0xD4] = { RM,0, "PADDQ %x,%X" }, +[0xD5] = { RM,0, "PMULLW %x,%X" }, +[0xD6] = { RM,0, "MOVQ %X,%x" }, +[0xE6] = { RM,0, "CVTTPD2PL %x,%X" }, +[0xE7] = { RM,0, "MOVNTO %X,%e" }, +[0xF7] = { RM,0, "MASKMOVOU %x,%X" }, }; static Optable optabF20F[256]= { -[0x10] RM,0, "MOVSD %x,%X", -[0x11] RM,0, "MOVSD %X,%x", -[0x2A] RM,0, "CVTS%S2SD %e,%X", -[0x2C] RM,0, "CVTTSD2S%S %x,%r", -[0x2D] RM,0, "CVTSD2S%S %x,%r", -[0x5A] RM,0, "CVTSD2SS %x,%X", -[0x6F] RM,0, "MOVOU %x,%X", -[0x70] RM,Ib, "PSHUFLW %i,%x,%X", -[0x7F] RM,0, "MOVOU %X,%x", -[0xD6] RM,0, "MOVQOZX %M,%X", -[0xE6] RM,0, "CVTPD2PL %x,%X", +[0x10] = { RM,0, "MOVSD %x,%X" }, +[0x11] = { RM,0, "MOVSD %X,%x" }, +[0x2A] = { RM,0, "CVTS%S2SD %e,%X" }, +[0x2C] = { RM,0, "CVTTSD2S%S %x,%r" }, +[0x2D] = { RM,0, "CVTSD2S%S %x,%r" }, +[0x5A] = { RM,0, "CVTSD2SS %x,%X" }, +[0x6F] = { RM,0, "MOVOU %x,%X" }, +[0x70] = { RM,Ib, "PSHUFLW %i,%x,%X" }, +[0x7F] = { RM,0, "MOVOU %X,%x" }, +[0xD6] = { RM,0, "MOVQOZX %M,%X" }, +[0xE6] = { RM,0, "CVTPD2PL %x,%X" }, }; static Optable optabF30F[256]= { -[0x10] RM,0, "MOVSS %x,%X", -[0x11] RM,0, "MOVSS %X,%x", -[0x2A] RM,0, "CVTS%S2SS %e,%X", -[0x2C] RM,0, "CVTTSS2S%S %x,%r", -[0x2D] RM,0, "CVTSS2S%S %x,%r", -[0x5A] RM,0, "CVTSS2SD %x,%X", -[0x5B] RM,0, "CVTTPS2PL %x,%X", -[0x6F] RM,0, "MOVOU %x,%X", -[0x70] RM,Ib, "PSHUFHW %i,%x,%X", -[0x7E] RM,0, "MOVQOZX %x,%X", -[0x7F] RM,0, "MOVOU %X,%x", -[0xD6] RM,0, "MOVQOZX %m*,%X", -[0xE6] RM,0, "CVTPL2PD %x,%X", +[0x10] = { RM,0, "MOVSS %x,%X" }, +[0x11] = { RM,0, "MOVSS %X,%x" }, +[0x2A] = { RM,0, "CVTS%S2SS %e,%X" }, +[0x2C] = { RM,0, "CVTTSS2S%S %x,%r" }, +[0x2D] = { RM,0, "CVTSS2S%S %x,%r" }, +[0x5A] = { RM,0, "CVTSS2SD %x,%X" }, +[0x5B] = { RM,0, "CVTTPS2PL %x,%X" }, +[0x6F] = { RM,0, "MOVOU %x,%X" }, +[0x70] = { RM,Ib, "PSHUFHW %i,%x,%X" }, +[0x7E] = { RM,0, "MOVQOZX %x,%X" }, +[0x7F] = { RM,0, "MOVOU %X,%x" }, +[0xD6] = { RM,0, "MOVQOZX %m*,%X" }, +[0xE6] = { RM,0, "CVTPL2PD %x,%X" }, }; static Optable optab0F[256]= { -[0x00] RMOP,0, optab0F00, -[0x01] RMOP,0, optab0F01, -[0x02] RM,0, "LAR %e,%r", -[0x03] RM,0, "LSL %e,%r", -[0x05] 0,0, "SYSCALL", -[0x06] 0,0, "CLTS", -[0x07] 0,0, "SYSRET", -[0x08] 0,0, "INVD", -[0x09] 0,0, "WBINVD", -[0x0B] 0,0, "UD2", -[0x0F] RM,AUX, optab0F0F, /* 3DNow! */ -[0x10] RM,0, "MOVU%s %x,%X", -[0x11] RM,0, "MOVU%s %X,%x", -[0x12] RM,0, "MOV[H]L%s %x,%X", /* TO DO: H if source is XMM */ -[0x13] RM,0, "MOVL%s %X,%e", -[0x14] RM,0, "UNPCKL%s %x,%X", -[0x15] RM,0, "UNPCKH%s %x,%X", -[0x16] RM,0, "MOV[L]H%s %x,%X", /* TO DO: L if source is XMM */ -[0x17] RM,0, "MOVH%s %X,%x", -[0x20] RMR,0, "MOVL %C,%e", -[0x21] RMR,0, "MOVL %D,%e", -[0x22] RMR,0, "MOVL %e,%C", -[0x23] RMR,0, "MOVL %e,%D", -[0x24] RMR,0, "MOVL %T,%e", -[0x26] RMR,0, "MOVL %e,%T", -[0x28] RM,0, "MOVA%s %x,%X", -[0x29] RM,0, "MOVA%s %X,%x", -[0x2A] RM,0, "CVTPL2%s %m*,%X", -[0x2B] RM,0, "MOVNT%s %X,%e", -[0x2C] RM,0, "CVTT%s2PL %x,%M", -[0x2D] RM,0, "CVT%s2PL %x,%M", -[0x2E] RM,0, "UCOMISS %x,%X", -[0x2F] RM,0, "COMISS %x,%X", -[0x30] 0,0, "WRMSR", -[0x31] 0,0, "RDTSC", -[0x32] 0,0, "RDMSR", -[0x33] 0,0, "RDPMC", -[0x42] RM,0, "CMOVC %e,%r", /* CF */ -[0x43] RM,0, "CMOVNC %e,%r", /* ¬ CF */ -[0x44] RM,0, "CMOVZ %e,%r", /* ZF */ -[0x45] RM,0, "CMOVNZ %e,%r", /* ¬ ZF */ -[0x46] RM,0, "CMOVBE %e,%r", /* CF ∨ ZF */ -[0x47] RM,0, "CMOVA %e,%r", /* ¬CF ∧ ¬ZF */ -[0x48] RM,0, "CMOVS %e,%r", /* SF */ -[0x49] RM,0, "CMOVNS %e,%r", /* ¬ SF */ -[0x4A] RM,0, "CMOVP %e,%r", /* PF */ -[0x4B] RM,0, "CMOVNP %e,%r", /* ¬ PF */ -[0x4C] RM,0, "CMOVLT %e,%r", /* LT ≡ OF ≠SF */ -[0x4D] RM,0, "CMOVGE %e,%r", /* GE ≡ ZF ∨ SF */ -[0x4E] RM,0, "CMOVLE %e,%r", /* LE ≡ ZF ∨ LT */ -[0x4F] RM,0, "CMOVGT %e,%r", /* GT ≡ ¬ZF ∧ GE */ -[0x50] RM,0, "MOVMSK%s %X,%r", /* TO DO: check */ -[0x51] RM,0, "SQRT%s %x,%X", -[0x52] RM,0, "RSQRT%s %x,%X", -[0x53] RM,0, "RCP%s %x,%X", -[0x54] RM,0, "AND%s %x,%X", -[0x55] RM,0, "ANDN%s %x,%X", -[0x56] RM,0, "OR%s %x,%X", /* TO DO: S/D */ -[0x57] RM,0, "XOR%s %x,%X", /* S/D */ -[0x58] RM,0, "ADD%s %x,%X", /* S/P S/D */ -[0x59] RM,0, "MUL%s %x,%X", -[0x5A] RM,0, "CVTPS2PD %x,%X", -[0x5B] RM,0, "CVTPL2PS %x,%X", -[0x5C] RM,0, "SUB%s %x,%X", -[0x5D] RM,0, "MIN%s %x,%X", -[0x5E] RM,0, "DIV%s %x,%X", /* TO DO: S/P S/D */ -[0x5F] RM,0, "MAX%s %x,%X", -[0x60] RM,0, "PUNPCKLBW %m,%M", -[0x61] RM,0, "PUNPCKLWL %m,%M", -[0x62] RM,0, "PUNPCKLLQ %m,%M", -[0x63] RM,0, "PACKSSWB %m,%M", -[0x64] RM,0, "PCMPGTB %m,%M", -[0x65] RM,0, "PCMPGTW %m,%M", -[0x66] RM,0, "PCMPGTL %m,%M", -[0x67] RM,0, "PACKUSWB %m,%M", -[0x68] RM,0, "PUNPCKHBW %m,%M", -[0x69] RM,0, "PUNPCKHWL %m,%M", -[0x6A] RM,0, "PUNPCKHLQ %m,%M", -[0x6B] RM,0, "PACKSSLW %m,%M", -[0x6E] RM,0, "MOV%S %e,%M", -[0x6F] RM,0, "MOVQ %m,%M", -[0x70] RM,Ib, "PSHUFW %i,%m,%M", -[0x74] RM,0, "PCMPEQB %m,%M", -[0x75] RM,0, "PCMPEQW %m,%M", -[0x76] RM,0, "PCMPEQL %m,%M", -[0x7E] RM,0, "MOV%S %M,%e", -[0x7F] RM,0, "MOVQ %M,%m", -[0xAE] RMOP,0, optab0FAE, -[0xAA] 0,0, "RSM", -[0xB0] RM,0, "CMPXCHGB %r,%e", -[0xB1] RM,0, "CMPXCHG%S %r,%e", -[0xC0] RMB,0, "XADDB %r,%e", -[0xC1] RM,0, "XADD%S %r,%e", -[0xC2] RM,Ib, "CMP%s %x,%X,%#i", -[0xC3] RM,0, "MOVNTI%S %r,%e", -[0xC6] RM,Ib, "SHUF%s %i,%x,%X", -[0xC8] 0,0, "BSWAP AX", -[0xC9] 0,0, "BSWAP CX", -[0xCA] 0,0, "BSWAP DX", -[0xCB] 0,0, "BSWAP BX", -[0xCC] 0,0, "BSWAP SP", -[0xCD] 0,0, "BSWAP BP", -[0xCE] 0,0, "BSWAP SI", -[0xCF] 0,0, "BSWAP DI", -[0xD1] RM,0, "PSRLW %m,%M", -[0xD2] RM,0, "PSRLL %m,%M", -[0xD3] RM,0, "PSRLQ %m,%M", -[0xD5] RM,0, "PMULLW %m,%M", -[0xD6] RM,0, "MOVQOZX %m*,%X", -[0xD7] RM,0, "PMOVMSKB %m,%r", -[0xD8] RM,0, "PSUBUSB %m,%M", -[0xD9] RM,0, "PSUBUSW %m,%M", -[0xDA] RM,0, "PMINUB %m,%M", -[0xDB] RM,0, "PAND %m,%M", -[0xDC] RM,0, "PADDUSB %m,%M", -[0xDD] RM,0, "PADDUSW %m,%M", -[0xDE] RM,0, "PMAXUB %m,%M", -[0xDF] RM,0, "PANDN %m,%M", -[0xE0] RM,0, "PAVGB %m,%M", -[0xE1] RM,0, "PSRAW %m,%M", -[0xE2] RM,0, "PSRAL %m,%M", -[0xE3] RM,0, "PAVGW %m,%M", -[0xE4] RM,0, "PMULHUW %m,%M", -[0xE5] RM,0, "PMULHW %m,%M", -[0xE7] RM,0, "MOVNTQ %M,%e", -[0xE8] RM,0, "PSUBSB %m,%M", -[0xE9] RM,0, "PSUBSW %m,%M", -[0xEA] RM,0, "PMINSW %m,%M", -[0xEB] RM,0, "POR %m,%M", -[0xEC] RM,0, "PADDSB %m,%M", -[0xED] RM,0, "PADDSW %m,%M", -[0xEE] RM,0, "PMAXSW %m,%M", -[0xEF] RM,0, "PXOR %m,%M", -[0xF1] RM,0, "PSLLW %m,%M", -[0xF2] RM,0, "PSLLL %m,%M", -[0xF3] RM,0, "PSLLQ %m,%M", -[0xF4] RM,0, "PMULULQ %m,%M", -[0xF5] RM,0, "PMADDWL %m,%M", -[0xF6] RM,0, "PSADBW %m,%M", -[0xF7] RMR,0, "MASKMOVQ %m,%M", -[0xF8] RM,0, "PSUBB %m,%M", -[0xF9] RM,0, "PSUBW %m,%M", -[0xFA] RM,0, "PSUBL %m,%M", -[0xFC] RM,0, "PADDB %m,%M", -[0xFD] RM,0, "PADDW %m,%M", -[0xFE] RM,0, "PADDL %m,%M", - -[0x80] Iwds,0, "JOS %p", -[0x81] Iwds,0, "JOC %p", -[0x82] Iwds,0, "JCS %p", -[0x83] Iwds,0, "JCC %p", -[0x84] Iwds,0, "JEQ %p", -[0x85] Iwds,0, "JNE %p", -[0x86] Iwds,0, "JLS %p", -[0x87] Iwds,0, "JHI %p", -[0x88] Iwds,0, "JMI %p", -[0x89] Iwds,0, "JPL %p", -[0x8a] Iwds,0, "JPS %p", -[0x8b] Iwds,0, "JPC %p", -[0x8c] Iwds,0, "JLT %p", -[0x8d] Iwds,0, "JGE %p", -[0x8e] Iwds,0, "JLE %p", -[0x8f] Iwds,0, "JGT %p", -[0x90] RMB,0, "SETOS %e", -[0x91] RMB,0, "SETOC %e", -[0x92] RMB,0, "SETCS %e", -[0x93] RMB,0, "SETCC %e", -[0x94] RMB,0, "SETEQ %e", -[0x95] RMB,0, "SETNE %e", -[0x96] RMB,0, "SETLS %e", -[0x97] RMB,0, "SETHI %e", -[0x98] RMB,0, "SETMI %e", -[0x99] RMB,0, "SETPL %e", -[0x9a] RMB,0, "SETPS %e", -[0x9b] RMB,0, "SETPC %e", -[0x9c] RMB,0, "SETLT %e", -[0x9d] RMB,0, "SETGE %e", -[0x9e] RMB,0, "SETLE %e", -[0x9f] RMB,0, "SETGT %e", -[0xa0] 0,0, "PUSHL FS", -[0xa1] 0,0, "POPL FS", -[0xa2] 0,0, "CPUID", -[0xa3] RM,0, "BT%S %r,%e", -[0xa4] RM,Ib, "SHLD%S %r,%i,%e", -[0xa5] RM,0, "SHLD%S %r,CL,%e", -[0xa8] 0,0, "PUSHL GS", -[0xa9] 0,0, "POPL GS", -[0xab] RM,0, "BTS%S %r,%e", -[0xac] RM,Ib, "SHRD%S %r,%i,%e", -[0xad] RM,0, "SHRD%S %r,CL,%e", -[0xaf] RM,0, "IMUL%S %e,%r", -[0xb2] RMM,0, "LSS %e,%r", -[0xb3] RM,0, "BTR%S %r,%e", -[0xb4] RMM,0, "LFS %e,%r", -[0xb5] RMM,0, "LGS %e,%r", -[0xb6] RMB,0, "MOVBZX %e,%R", -[0xb7] RM,0, "MOVWZX %e,%R", -[0xba] RMOP,0, optab0FBA, -[0xbb] RM,0, "BTC%S %e,%r", -[0xbc] RM,0, "BSF%S %e,%r", -[0xbd] RM,0, "BSR%S %e,%r", -[0xbe] RMB,0, "MOVBSX %e,%R", -[0xbf] RM,0, "MOVWSX %e,%R", -[0xc7] RMOP,0, optab0FC7, +[0x00] = { RMOP,0, optab0F00 }, +[0x01] = { RMOP,0, optab0F01 }, +[0x02] = { RM,0, "LAR %e,%r" }, +[0x03] = { RM,0, "LSL %e,%r" }, +[0x05] = { 0,0, "SYSCALL" }, +[0x06] = { 0,0, "CLTS" }, +[0x07] = { 0,0, "SYSRET" }, +[0x08] = { 0,0, "INVD" }, +[0x09] = { 0,0, "WBINVD" }, +[0x0B] = { 0,0, "UD2" }, +[0x0F] = { RM,AUX, optab0F0F }, /* 3DNow! */ +[0x10] = { RM,0, "MOVU%s %x,%X" }, +[0x11] = { RM,0, "MOVU%s %X,%x" }, +[0x12] = { RM,0, "MOV[H]L%s %x,%X" }, /* TO DO: H if source is XMM */ +[0x13] = { RM,0, "MOVL%s %X,%e" }, +[0x14] = { RM,0, "UNPCKL%s %x,%X" }, +[0x15] = { RM,0, "UNPCKH%s %x,%X" }, +[0x16] = { RM,0, "MOV[L]H%s %x,%X" }, /* TO DO: L if source is XMM */ +[0x17] = { RM,0, "MOVH%s %X,%x" }, +[0x20] = { RMR,0, "MOVL %C,%e" }, +[0x21] = { RMR,0, "MOVL %D,%e" }, +[0x22] = { RMR,0, "MOVL %e,%C" }, +[0x23] = { RMR,0, "MOVL %e,%D" }, +[0x24] = { RMR,0, "MOVL %T,%e" }, +[0x26] = { RMR,0, "MOVL %e,%T" }, +[0x28] = { RM,0, "MOVA%s %x,%X" }, +[0x29] = { RM,0, "MOVA%s %X,%x" }, +[0x2A] = { RM,0, "CVTPL2%s %m*,%X" }, +[0x2B] = { RM,0, "MOVNT%s %X,%e" }, +[0x2C] = { RM,0, "CVTT%s2PL %x,%M" }, +[0x2D] = { RM,0, "CVT%s2PL %x,%M" }, +[0x2E] = { RM,0, "UCOMISS %x,%X" }, +[0x2F] = { RM,0, "COMISS %x,%X" }, +[0x30] = { 0,0, "WRMSR" }, +[0x31] = { 0,0, "RDTSC" }, +[0x32] = { 0,0, "RDMSR" }, +[0x33] = { 0,0, "RDPMC" }, +[0x42] = { RM,0, "CMOVC %e,%r" }, /* CF */ +[0x43] = { RM,0, "CMOVNC %e,%r" }, /* ¬ CF */ +[0x44] = { RM,0, "CMOVZ %e,%r" }, /* ZF */ +[0x45] = { RM,0, "CMOVNZ %e,%r" }, /* ¬ ZF */ +[0x46] = { RM,0, "CMOVBE %e,%r" }, /* CF ∨ ZF */ +[0x47] = { RM,0, "CMOVA %e,%r" }, /* ¬CF ∧ ¬ZF */ +[0x48] = { RM,0, "CMOVS %e,%r" }, /* SF */ +[0x49] = { RM,0, "CMOVNS %e,%r" }, /* ¬ SF */ +[0x4A] = { RM,0, "CMOVP %e,%r" }, /* PF */ +[0x4B] = { RM,0, "CMOVNP %e,%r" }, /* ¬ PF */ +[0x4C] = { RM,0, "CMOVLT %e,%r" }, /* LT ≡ OF ≠SF */ +[0x4D] = { RM,0, "CMOVGE %e,%r" }, /* GE ≡ ZF ∨ SF */ +[0x4E] = { RM,0, "CMOVLE %e,%r" }, /* LE ≡ ZF ∨ LT */ +[0x4F] = { RM,0, "CMOVGT %e,%r" }, /* GT ≡ ¬ZF ∧ GE */ +[0x50] = { RM,0, "MOVMSK%s %X,%r" }, /* TO DO: check */ +[0x51] = { RM,0, "SQRT%s %x,%X" }, +[0x52] = { RM,0, "RSQRT%s %x,%X" }, +[0x53] = { RM,0, "RCP%s %x,%X" }, +[0x54] = { RM,0, "AND%s %x,%X" }, +[0x55] = { RM,0, "ANDN%s %x,%X" }, +[0x56] = { RM,0, "OR%s %x,%X" }, /* TO DO: S/D */ +[0x57] = { RM,0, "XOR%s %x,%X" }, /* S/D */ +[0x58] = { RM,0, "ADD%s %x,%X" }, /* S/P S/D */ +[0x59] = { RM,0, "MUL%s %x,%X" }, +[0x5A] = { RM,0, "CVTPS2PD %x,%X" }, +[0x5B] = { RM,0, "CVTPL2PS %x,%X" }, +[0x5C] = { RM,0, "SUB%s %x,%X" }, +[0x5D] = { RM,0, "MIN%s %x,%X" }, +[0x5E] = { RM,0, "DIV%s %x,%X" }, /* TO DO: S/P S/D */ +[0x5F] = { RM,0, "MAX%s %x,%X" }, +[0x60] = { RM,0, "PUNPCKLBW %m,%M" }, +[0x61] = { RM,0, "PUNPCKLWL %m,%M" }, +[0x62] = { RM,0, "PUNPCKLLQ %m,%M" }, +[0x63] = { RM,0, "PACKSSWB %m,%M" }, +[0x64] = { RM,0, "PCMPGTB %m,%M" }, +[0x65] = { RM,0, "PCMPGTW %m,%M" }, +[0x66] = { RM,0, "PCMPGTL %m,%M" }, +[0x67] = { RM,0, "PACKUSWB %m,%M" }, +[0x68] = { RM,0, "PUNPCKHBW %m,%M" }, +[0x69] = { RM,0, "PUNPCKHWL %m,%M" }, +[0x6A] = { RM,0, "PUNPCKHLQ %m,%M" }, +[0x6B] = { RM,0, "PACKSSLW %m,%M" }, +[0x6E] = { RM,0, "MOV%S %e,%M" }, +[0x6F] = { RM,0, "MOVQ %m,%M" }, +[0x70] = { RM,Ib, "PSHUFW %i,%m,%M" }, +[0x74] = { RM,0, "PCMPEQB %m,%M" }, +[0x75] = { RM,0, "PCMPEQW %m,%M" }, +[0x76] = { RM,0, "PCMPEQL %m,%M" }, +[0x7E] = { RM,0, "MOV%S %M,%e" }, +[0x7F] = { RM,0, "MOVQ %M,%m" }, +[0xAE] = { RMOP,0, optab0FAE }, +[0xAA] = { 0,0, "RSM" }, +[0xB0] = { RM,0, "CMPXCHGB %r,%e" }, +[0xB1] = { RM,0, "CMPXCHG%S %r,%e" }, +[0xC0] = { RMB,0, "XADDB %r,%e" }, +[0xC1] = { RM,0, "XADD%S %r,%e" }, +[0xC2] = { RM,Ib, "CMP%s %x,%X,%#i" }, +[0xC3] = { RM,0, "MOVNTI%S %r,%e" }, +[0xC6] = { RM,Ib, "SHUF%s %i,%x,%X" }, +[0xC8] = { 0,0, "BSWAP AX" }, +[0xC9] = { 0,0, "BSWAP CX" }, +[0xCA] = { 0,0, "BSWAP DX" }, +[0xCB] = { 0,0, "BSWAP BX" }, +[0xCC] = { 0,0, "BSWAP SP" }, +[0xCD] = { 0,0, "BSWAP BP" }, +[0xCE] = { 0,0, "BSWAP SI" }, +[0xCF] = { 0,0, "BSWAP DI" }, +[0xD1] = { RM,0, "PSRLW %m,%M" }, +[0xD2] = { RM,0, "PSRLL %m,%M" }, +[0xD3] = { RM,0, "PSRLQ %m,%M" }, +[0xD5] = { RM,0, "PMULLW %m,%M" }, +[0xD6] = { RM,0, "MOVQOZX %m*,%X" }, +[0xD7] = { RM,0, "PMOVMSKB %m,%r" }, +[0xD8] = { RM,0, "PSUBUSB %m,%M" }, +[0xD9] = { RM,0, "PSUBUSW %m,%M" }, +[0xDA] = { RM,0, "PMINUB %m,%M" }, +[0xDB] = { RM,0, "PAND %m,%M" }, +[0xDC] = { RM,0, "PADDUSB %m,%M" }, +[0xDD] = { RM,0, "PADDUSW %m,%M" }, +[0xDE] = { RM,0, "PMAXUB %m,%M" }, +[0xDF] = { RM,0, "PANDN %m,%M" }, +[0xE0] = { RM,0, "PAVGB %m,%M" }, +[0xE1] = { RM,0, "PSRAW %m,%M" }, +[0xE2] = { RM,0, "PSRAL %m,%M" }, +[0xE3] = { RM,0, "PAVGW %m,%M" }, +[0xE4] = { RM,0, "PMULHUW %m,%M" }, +[0xE5] = { RM,0, "PMULHW %m,%M" }, +[0xE7] = { RM,0, "MOVNTQ %M,%e" }, +[0xE8] = { RM,0, "PSUBSB %m,%M" }, +[0xE9] = { RM,0, "PSUBSW %m,%M" }, +[0xEA] = { RM,0, "PMINSW %m,%M" }, +[0xEB] = { RM,0, "POR %m,%M" }, +[0xEC] = { RM,0, "PADDSB %m,%M" }, +[0xED] = { RM,0, "PADDSW %m,%M" }, +[0xEE] = { RM,0, "PMAXSW %m,%M" }, +[0xEF] = { RM,0, "PXOR %m,%M" }, +[0xF1] = { RM,0, "PSLLW %m,%M" }, +[0xF2] = { RM,0, "PSLLL %m,%M" }, +[0xF3] = { RM,0, "PSLLQ %m,%M" }, +[0xF4] = { RM,0, "PMULULQ %m,%M" }, +[0xF5] = { RM,0, "PMADDWL %m,%M" }, +[0xF6] = { RM,0, "PSADBW %m,%M" }, +[0xF7] = { RMR,0, "MASKMOVQ %m,%M" }, +[0xF8] = { RM,0, "PSUBB %m,%M" }, +[0xF9] = { RM,0, "PSUBW %m,%M" }, +[0xFA] = { RM,0, "PSUBL %m,%M" }, +[0xFC] = { RM,0, "PADDB %m,%M" }, +[0xFD] = { RM,0, "PADDW %m,%M" }, +[0xFE] = { RM,0, "PADDL %m,%M" }, + +[0x80] = { Iwds,0, "JOS %p" }, +[0x81] = { Iwds,0, "JOC %p" }, +[0x82] = { Iwds,0, "JCS %p" }, +[0x83] = { Iwds,0, "JCC %p" }, +[0x84] = { Iwds,0, "JEQ %p" }, +[0x85] = { Iwds,0, "JNE %p" }, +[0x86] = { Iwds,0, "JLS %p" }, +[0x87] = { Iwds,0, "JHI %p" }, +[0x88] = { Iwds,0, "JMI %p" }, +[0x89] = { Iwds,0, "JPL %p" }, +[0x8a] = { Iwds,0, "JPS %p" }, +[0x8b] = { Iwds,0, "JPC %p" }, +[0x8c] = { Iwds,0, "JLT %p" }, +[0x8d] = { Iwds,0, "JGE %p" }, +[0x8e] = { Iwds,0, "JLE %p" }, +[0x8f] = { Iwds,0, "JGT %p" }, +[0x90] = { RMB,0, "SETOS %e" }, +[0x91] = { RMB,0, "SETOC %e" }, +[0x92] = { RMB,0, "SETCS %e" }, +[0x93] = { RMB,0, "SETCC %e" }, +[0x94] = { RMB,0, "SETEQ %e" }, +[0x95] = { RMB,0, "SETNE %e" }, +[0x96] = { RMB,0, "SETLS %e" }, +[0x97] = { RMB,0, "SETHI %e" }, +[0x98] = { RMB,0, "SETMI %e" }, +[0x99] = { RMB,0, "SETPL %e" }, +[0x9a] = { RMB,0, "SETPS %e" }, +[0x9b] = { RMB,0, "SETPC %e" }, +[0x9c] = { RMB,0, "SETLT %e" }, +[0x9d] = { RMB,0, "SETGE %e" }, +[0x9e] = { RMB,0, "SETLE %e" }, +[0x9f] = { RMB,0, "SETGT %e" }, +[0xa0] = { 0,0, "PUSHL FS" }, +[0xa1] = { 0,0, "POPL FS" }, +[0xa2] = { 0,0, "CPUID" }, +[0xa3] = { RM,0, "BT%S %r,%e" }, +[0xa4] = { RM,Ib, "SHLD%S %r,%i,%e" }, +[0xa5] = { RM,0, "SHLD%S %r,CL,%e" }, +[0xa8] = { 0,0, "PUSHL GS" }, +[0xa9] = { 0,0, "POPL GS" }, +[0xab] = { RM,0, "BTS%S %r,%e" }, +[0xac] = { RM,Ib, "SHRD%S %r,%i,%e" }, +[0xad] = { RM,0, "SHRD%S %r,CL,%e" }, +[0xaf] = { RM,0, "IMUL%S %e,%r" }, +[0xb2] = { RMM,0, "LSS %e,%r" }, +[0xb3] = { RM,0, "BTR%S %r,%e" }, +[0xb4] = { RMM,0, "LFS %e,%r" }, +[0xb5] = { RMM,0, "LGS %e,%r" }, +[0xb6] = { RMB,0, "MOVBZX %e,%R" }, +[0xb7] = { RM,0, "MOVWZX %e,%R" }, +[0xba] = { RMOP,0, optab0FBA }, +[0xbb] = { RM,0, "BTC%S %e,%r" }, +[0xbc] = { RM,0, "BSF%S %e,%r" }, +[0xbd] = { RM,0, "BSR%S %e,%r" }, +[0xbe] = { RMB,0, "MOVBSX %e,%R" }, +[0xbf] = { RM,0, "MOVWSX %e,%R" }, +[0xc7] = { RMOP,0, optab0FC7 }, }; static Optable optab80[8]= { -[0x00] Ib,0, "ADDB %i,%e", -[0x01] Ib,0, "ORB %i,%e", -[0x02] Ib,0, "ADCB %i,%e", -[0x03] Ib,0, "SBBB %i,%e", -[0x04] Ib,0, "ANDB %i,%e", -[0x05] Ib,0, "SUBB %i,%e", -[0x06] Ib,0, "XORB %i,%e", -[0x07] Ib,0, "CMPB %e,%i", +[0x00] = { Ib,0, "ADDB %i,%e" }, +[0x01] = { Ib,0, "ORB %i,%e" }, +[0x02] = { Ib,0, "ADCB %i,%e" }, +[0x03] = { Ib,0, "SBBB %i,%e" }, +[0x04] = { Ib,0, "ANDB %i,%e" }, +[0x05] = { Ib,0, "SUBB %i,%e" }, +[0x06] = { Ib,0, "XORB %i,%e" }, +[0x07] = { Ib,0, "CMPB %e,%i" }, }; static Optable optab81[8]= { -[0x00] Iwd,0, "ADD%S %i,%e", -[0x01] Iwd,0, "OR%S %i,%e", -[0x02] Iwd,0, "ADC%S %i,%e", -[0x03] Iwd,0, "SBB%S %i,%e", -[0x04] Iwd,0, "AND%S %i,%e", -[0x05] Iwd,0, "SUB%S %i,%e", -[0x06] Iwd,0, "XOR%S %i,%e", -[0x07] Iwd,0, "CMP%S %e,%i", +[0x00] = { Iwd,0, "ADD%S %i,%e" }, +[0x01] = { Iwd,0, "OR%S %i,%e" }, +[0x02] = { Iwd,0, "ADC%S %i,%e" }, +[0x03] = { Iwd,0, "SBB%S %i,%e" }, +[0x04] = { Iwd,0, "AND%S %i,%e" }, +[0x05] = { Iwd,0, "SUB%S %i,%e" }, +[0x06] = { Iwd,0, "XOR%S %i,%e" }, +[0x07] = { Iwd,0, "CMP%S %e,%i" }, }; static Optable optab83[8]= { -[0x00] Ibs,0, "ADD%S %i,%e", -[0x01] Ibs,0, "OR%S %i,%e", -[0x02] Ibs,0, "ADC%S %i,%e", -[0x03] Ibs,0, "SBB%S %i,%e", -[0x04] Ibs,0, "AND%S %i,%e", -[0x05] Ibs,0, "SUB%S %i,%e", -[0x06] Ibs,0, "XOR%S %i,%e", -[0x07] Ibs,0, "CMP%S %e,%i", +[0x00] = { Ibs,0, "ADD%S %i,%e" }, +[0x01] = { Ibs,0, "OR%S %i,%e" }, +[0x02] = { Ibs,0, "ADC%S %i,%e" }, +[0x03] = { Ibs,0, "SBB%S %i,%e" }, +[0x04] = { Ibs,0, "AND%S %i,%e" }, +[0x05] = { Ibs,0, "SUB%S %i,%e" }, +[0x06] = { Ibs,0, "XOR%S %i,%e" }, +[0x07] = { Ibs,0, "CMP%S %e,%i" }, }; static Optable optabC0[8] = { -[0x00] Ib,0, "ROLB %i,%e", -[0x01] Ib,0, "RORB %i,%e", -[0x02] Ib,0, "RCLB %i,%e", -[0x03] Ib,0, "RCRB %i,%e", -[0x04] Ib,0, "SHLB %i,%e", -[0x05] Ib,0, "SHRB %i,%e", -[0x07] Ib,0, "SARB %i,%e", +[0x00] = { Ib,0, "ROLB %i,%e" }, +[0x01] = { Ib,0, "RORB %i,%e" }, +[0x02] = { Ib,0, "RCLB %i,%e" }, +[0x03] = { Ib,0, "RCRB %i,%e" }, +[0x04] = { Ib,0, "SHLB %i,%e" }, +[0x05] = { Ib,0, "SHRB %i,%e" }, +[0x07] = { Ib,0, "SARB %i,%e" }, }; static Optable optabC1[8] = { -[0x00] Ib,0, "ROL%S %i,%e", -[0x01] Ib,0, "ROR%S %i,%e", -[0x02] Ib,0, "RCL%S %i,%e", -[0x03] Ib,0, "RCR%S %i,%e", -[0x04] Ib,0, "SHL%S %i,%e", -[0x05] Ib,0, "SHR%S %i,%e", -[0x07] Ib,0, "SAR%S %i,%e", +[0x00] = { Ib,0, "ROL%S %i,%e" }, +[0x01] = { Ib,0, "ROR%S %i,%e" }, +[0x02] = { Ib,0, "RCL%S %i,%e" }, +[0x03] = { Ib,0, "RCR%S %i,%e" }, +[0x04] = { Ib,0, "SHL%S %i,%e" }, +[0x05] = { Ib,0, "SHR%S %i,%e" }, +[0x07] = { Ib,0, "SAR%S %i,%e" }, }; static Optable optabD0[8] = { -[0x00] 0,0, "ROLB %e", -[0x01] 0,0, "RORB %e", -[0x02] 0,0, "RCLB %e", -[0x03] 0,0, "RCRB %e", -[0x04] 0,0, "SHLB %e", -[0x05] 0,0, "SHRB %e", -[0x07] 0,0, "SARB %e", +[0x00] = { 0,0, "ROLB %e" }, +[0x01] = { 0,0, "RORB %e" }, +[0x02] = { 0,0, "RCLB %e" }, +[0x03] = { 0,0, "RCRB %e" }, +[0x04] = { 0,0, "SHLB %e" }, +[0x05] = { 0,0, "SHRB %e" }, +[0x07] = { 0,0, "SARB %e" }, }; static Optable optabD1[8] = { -[0x00] 0,0, "ROL%S %e", -[0x01] 0,0, "ROR%S %e", -[0x02] 0,0, "RCL%S %e", -[0x03] 0,0, "RCR%S %e", -[0x04] 0,0, "SHL%S %e", -[0x05] 0,0, "SHR%S %e", -[0x07] 0,0, "SAR%S %e", +[0x00] = { 0,0, "ROL%S %e" }, +[0x01] = { 0,0, "ROR%S %e" }, +[0x02] = { 0,0, "RCL%S %e" }, +[0x03] = { 0,0, "RCR%S %e" }, +[0x04] = { 0,0, "SHL%S %e" }, +[0x05] = { 0,0, "SHR%S %e" }, +[0x07] = { 0,0, "SAR%S %e" }, }; static Optable optabD2[8] = { -[0x00] 0,0, "ROLB CL,%e", -[0x01] 0,0, "RORB CL,%e", -[0x02] 0,0, "RCLB CL,%e", -[0x03] 0,0, "RCRB CL,%e", -[0x04] 0,0, "SHLB CL,%e", -[0x05] 0,0, "SHRB CL,%e", -[0x07] 0,0, "SARB CL,%e", +[0x00] = { 0,0, "ROLB CL,%e" }, +[0x01] = { 0,0, "RORB CL,%e" }, +[0x02] = { 0,0, "RCLB CL,%e" }, +[0x03] = { 0,0, "RCRB CL,%e" }, +[0x04] = { 0,0, "SHLB CL,%e" }, +[0x05] = { 0,0, "SHRB CL,%e" }, +[0x07] = { 0,0, "SARB CL,%e" }, }; static Optable optabD3[8] = { -[0x00] 0,0, "ROL%S CL,%e", -[0x01] 0,0, "ROR%S CL,%e", -[0x02] 0,0, "RCL%S CL,%e", -[0x03] 0,0, "RCR%S CL,%e", -[0x04] 0,0, "SHL%S CL,%e", -[0x05] 0,0, "SHR%S CL,%e", -[0x07] 0,0, "SAR%S CL,%e", +[0x00] = { 0,0, "ROL%S CL,%e" }, +[0x01] = { 0,0, "ROR%S CL,%e" }, +[0x02] = { 0,0, "RCL%S CL,%e" }, +[0x03] = { 0,0, "RCR%S CL,%e" }, +[0x04] = { 0,0, "SHL%S CL,%e" }, +[0x05] = { 0,0, "SHR%S CL,%e" }, +[0x07] = { 0,0, "SAR%S CL,%e" }, }; static Optable optabD8[8+8] = { -[0x00] 0,0, "FADDF %e,F0", -[0x01] 0,0, "FMULF %e,F0", -[0x02] 0,0, "FCOMF %e,F0", -[0x03] 0,0, "FCOMFP %e,F0", -[0x04] 0,0, "FSUBF %e,F0", -[0x05] 0,0, "FSUBRF %e,F0", -[0x06] 0,0, "FDIVF %e,F0", -[0x07] 0,0, "FDIVRF %e,F0", -[0x08] 0,0, "FADDD %f,F0", -[0x09] 0,0, "FMULD %f,F0", -[0x0a] 0,0, "FCOMD %f,F0", -[0x0b] 0,0, "FCOMPD %f,F0", -[0x0c] 0,0, "FSUBD %f,F0", -[0x0d] 0,0, "FSUBRD %f,F0", -[0x0e] 0,0, "FDIVD %f,F0", -[0x0f] 0,0, "FDIVRD %f,F0", +[0x00] = { 0,0, "FADDF %e,F0" }, +[0x01] = { 0,0, "FMULF %e,F0" }, +[0x02] = { 0,0, "FCOMF %e,F0" }, +[0x03] = { 0,0, "FCOMFP %e,F0" }, +[0x04] = { 0,0, "FSUBF %e,F0" }, +[0x05] = { 0,0, "FSUBRF %e,F0" }, +[0x06] = { 0,0, "FDIVF %e,F0" }, +[0x07] = { 0,0, "FDIVRF %e,F0" }, +[0x08] = { 0,0, "FADDD %f,F0" }, +[0x09] = { 0,0, "FMULD %f,F0" }, +[0x0a] = { 0,0, "FCOMD %f,F0" }, +[0x0b] = { 0,0, "FCOMPD %f,F0" }, +[0x0c] = { 0,0, "FSUBD %f,F0" }, +[0x0d] = { 0,0, "FSUBRD %f,F0" }, +[0x0e] = { 0,0, "FDIVD %f,F0" }, +[0x0f] = { 0,0, "FDIVRD %f,F0" }, }; /* * optabD9 and optabDB use the following encoding: @@ -940,455 +940,455 @@ static Optable optabD8[8+8] = */ static Optable optabD9[64+8] = { -[0x00] 0,0, "FMOVF %e,F0", -[0x02] 0,0, "FMOVF F0,%e", -[0x03] 0,0, "FMOVFP F0,%e", -[0x04] 0,0, "FLDENV%S %e", -[0x05] 0,0, "FLDCW %e", -[0x06] 0,0, "FSTENV%S %e", -[0x07] 0,0, "FSTCW %e", -[0x08] 0,0, "FMOVD F0,F0", /* Mod R/M = 11xx xxxx*/ -[0x09] 0,0, "FMOVD F1,F0", -[0x0a] 0,0, "FMOVD F2,F0", -[0x0b] 0,0, "FMOVD F3,F0", -[0x0c] 0,0, "FMOVD F4,F0", -[0x0d] 0,0, "FMOVD F5,F0", -[0x0e] 0,0, "FMOVD F6,F0", -[0x0f] 0,0, "FMOVD F7,F0", -[0x10] 0,0, "FXCHD F0,F0", -[0x11] 0,0, "FXCHD F1,F0", -[0x12] 0,0, "FXCHD F2,F0", -[0x13] 0,0, "FXCHD F3,F0", -[0x14] 0,0, "FXCHD F4,F0", -[0x15] 0,0, "FXCHD F5,F0", -[0x16] 0,0, "FXCHD F6,F0", -[0x17] 0,0, "FXCHD F7,F0", -[0x18] 0,0, "FNOP", -[0x28] 0,0, "FCHS", -[0x29] 0,0, "FABS", -[0x2c] 0,0, "FTST", -[0x2d] 0,0, "FXAM", -[0x30] 0,0, "FLD1", -[0x31] 0,0, "FLDL2T", -[0x32] 0,0, "FLDL2E", -[0x33] 0,0, "FLDPI", -[0x34] 0,0, "FLDLG2", -[0x35] 0,0, "FLDLN2", -[0x36] 0,0, "FLDZ", -[0x38] 0,0, "F2XM1", -[0x39] 0,0, "FYL2X", -[0x3a] 0,0, "FPTAN", -[0x3b] 0,0, "FPATAN", -[0x3c] 0,0, "FXTRACT", -[0x3d] 0,0, "FPREM1", -[0x3e] 0,0, "FDECSTP", -[0x3f] 0,0, "FNCSTP", -[0x40] 0,0, "FPREM", -[0x41] 0,0, "FYL2XP1", -[0x42] 0,0, "FSQRT", -[0x43] 0,0, "FSINCOS", -[0x44] 0,0, "FRNDINT", -[0x45] 0,0, "FSCALE", -[0x46] 0,0, "FSIN", -[0x47] 0,0, "FCOS", +[0x00] = { 0,0, "FMOVF %e,F0" }, +[0x02] = { 0,0, "FMOVF F0,%e" }, +[0x03] = { 0,0, "FMOVFP F0,%e" }, +[0x04] = { 0,0, "FLDENV%S %e" }, +[0x05] = { 0,0, "FLDCW %e" }, +[0x06] = { 0,0, "FSTENV%S %e" }, +[0x07] = { 0,0, "FSTCW %e" }, +[0x08] = { 0,0, "FMOVD F0,F0" }, /* Mod R/M = 11xx xxxx*/ +[0x09] = { 0,0, "FMOVD F1,F0" }, +[0x0a] = { 0,0, "FMOVD F2,F0" }, +[0x0b] = { 0,0, "FMOVD F3,F0" }, +[0x0c] = { 0,0, "FMOVD F4,F0" }, +[0x0d] = { 0,0, "FMOVD F5,F0" }, +[0x0e] = { 0,0, "FMOVD F6,F0" }, +[0x0f] = { 0,0, "FMOVD F7,F0" }, +[0x10] = { 0,0, "FXCHD F0,F0" }, +[0x11] = { 0,0, "FXCHD F1,F0" }, +[0x12] = { 0,0, "FXCHD F2,F0" }, +[0x13] = { 0,0, "FXCHD F3,F0" }, +[0x14] = { 0,0, "FXCHD F4,F0" }, +[0x15] = { 0,0, "FXCHD F5,F0" }, +[0x16] = { 0,0, "FXCHD F6,F0" }, +[0x17] = { 0,0, "FXCHD F7,F0" }, +[0x18] = { 0,0, "FNOP" }, +[0x28] = { 0,0, "FCHS" }, +[0x29] = { 0,0, "FABS" }, +[0x2c] = { 0,0, "FTST" }, +[0x2d] = { 0,0, "FXAM" }, +[0x30] = { 0,0, "FLD1" }, +[0x31] = { 0,0, "FLDL2T" }, +[0x32] = { 0,0, "FLDL2E" }, +[0x33] = { 0,0, "FLDPI" }, +[0x34] = { 0,0, "FLDLG2" }, +[0x35] = { 0,0, "FLDLN2" }, +[0x36] = { 0,0, "FLDZ" }, +[0x38] = { 0,0, "F2XM1" }, +[0x39] = { 0,0, "FYL2X" }, +[0x3a] = { 0,0, "FPTAN" }, +[0x3b] = { 0,0, "FPATAN" }, +[0x3c] = { 0,0, "FXTRACT" }, +[0x3d] = { 0,0, "FPREM1" }, +[0x3e] = { 0,0, "FDECSTP" }, +[0x3f] = { 0,0, "FNCSTP" }, +[0x40] = { 0,0, "FPREM" }, +[0x41] = { 0,0, "FYL2XP1" }, +[0x42] = { 0,0, "FSQRT" }, +[0x43] = { 0,0, "FSINCOS" }, +[0x44] = { 0,0, "FRNDINT" }, +[0x45] = { 0,0, "FSCALE" }, +[0x46] = { 0,0, "FSIN" }, +[0x47] = { 0,0, "FCOS" }, }; static Optable optabDA[8+8] = { -[0x00] 0,0, "FADDL %e,F0", -[0x01] 0,0, "FMULL %e,F0", -[0x02] 0,0, "FCOML %e,F0", -[0x03] 0,0, "FCOMLP %e,F0", -[0x04] 0,0, "FSUBL %e,F0", -[0x05] 0,0, "FSUBRL %e,F0", -[0x06] 0,0, "FDIVL %e,F0", -[0x07] 0,0, "FDIVRL %e,F0", -[0x08] 0,0, "FCMOVCS %f,F0", -[0x09] 0,0, "FCMOVEQ %f,F0", -[0x0a] 0,0, "FCMOVLS %f,F0", -[0x0b] 0,0, "FCMOVUN %f,F0", -[0x0d] Op_R1,0, "FUCOMPP", +[0x00] = { 0,0, "FADDL %e,F0" }, +[0x01] = { 0,0, "FMULL %e,F0" }, +[0x02] = { 0,0, "FCOML %e,F0" }, +[0x03] = { 0,0, "FCOMLP %e,F0" }, +[0x04] = { 0,0, "FSUBL %e,F0" }, +[0x05] = { 0,0, "FSUBRL %e,F0" }, +[0x06] = { 0,0, "FDIVL %e,F0" }, +[0x07] = { 0,0, "FDIVRL %e,F0" }, +[0x08] = { 0,0, "FCMOVCS %f,F0" }, +[0x09] = { 0,0, "FCMOVEQ %f,F0" }, +[0x0a] = { 0,0, "FCMOVLS %f,F0" }, +[0x0b] = { 0,0, "FCMOVUN %f,F0" }, +[0x0d] = { Op_R1,0, "FUCOMPP" }, }; static Optable optabDB[8+64] = { -[0x00] 0,0, "FMOVL %e,F0", -[0x02] 0,0, "FMOVL F0,%e", -[0x03] 0,0, "FMOVLP F0,%e", -[0x05] 0,0, "FMOVX %e,F0", -[0x07] 0,0, "FMOVXP F0,%e", -[0x08] 0,0, "FCMOVCC %f,F0", -[0x09] 0,0, "FCMOVNE %f,F0", -[0x0a] 0,0, "FCMOVHI %f,F0", -[0x0b] 0,0, "FCMOVNU %f,F0", -[0x0d] 0,0, "FUCOMI F0,%f", -[0x0e] 0,0, "FCOMI F0,%f", -[0x2a] 0,0, "FCLEX", -[0x2b] 0,0, "FINIT", +[0x00] = { 0,0, "FMOVL %e,F0" }, +[0x02] = { 0,0, "FMOVL F0,%e" }, +[0x03] = { 0,0, "FMOVLP F0,%e" }, +[0x05] = { 0,0, "FMOVX %e,F0" }, +[0x07] = { 0,0, "FMOVXP F0,%e" }, +[0x08] = { 0,0, "FCMOVCC %f,F0" }, +[0x09] = { 0,0, "FCMOVNE %f,F0" }, +[0x0a] = { 0,0, "FCMOVHI %f,F0" }, +[0x0b] = { 0,0, "FCMOVNU %f,F0" }, +[0x0d] = { 0,0, "FUCOMI F0,%f" }, +[0x0e] = { 0,0, "FCOMI F0,%f" }, +[0x2a] = { 0,0, "FCLEX" }, +[0x2b] = { 0,0, "FINIT" }, }; static Optable optabDC[8+8] = { -[0x00] 0,0, "FADDD %e,F0", -[0x01] 0,0, "FMULD %e,F0", -[0x02] 0,0, "FCOMD %e,F0", -[0x03] 0,0, "FCOMDP %e,F0", -[0x04] 0,0, "FSUBD %e,F0", -[0x05] 0,0, "FSUBRD %e,F0", -[0x06] 0,0, "FDIVD %e,F0", -[0x07] 0,0, "FDIVRD %e,F0", -[0x08] 0,0, "FADDD F0,%f", -[0x09] 0,0, "FMULD F0,%f", -[0x0c] 0,0, "FSUBRD F0,%f", -[0x0d] 0,0, "FSUBD F0,%f", -[0x0e] 0,0, "FDIVRD F0,%f", -[0x0f] 0,0, "FDIVD F0,%f", +[0x00] = { 0,0, "FADDD %e,F0" }, +[0x01] = { 0,0, "FMULD %e,F0" }, +[0x02] = { 0,0, "FCOMD %e,F0" }, +[0x03] = { 0,0, "FCOMDP %e,F0" }, +[0x04] = { 0,0, "FSUBD %e,F0" }, +[0x05] = { 0,0, "FSUBRD %e,F0" }, +[0x06] = { 0,0, "FDIVD %e,F0" }, +[0x07] = { 0,0, "FDIVRD %e,F0" }, +[0x08] = { 0,0, "FADDD F0,%f" }, +[0x09] = { 0,0, "FMULD F0,%f" }, +[0x0c] = { 0,0, "FSUBRD F0,%f" }, +[0x0d] = { 0,0, "FSUBD F0,%f" }, +[0x0e] = { 0,0, "FDIVRD F0,%f" }, +[0x0f] = { 0,0, "FDIVD F0,%f" }, }; static Optable optabDD[8+8] = { -[0x00] 0,0, "FMOVD %e,F0", -[0x02] 0,0, "FMOVD F0,%e", -[0x03] 0,0, "FMOVDP F0,%e", -[0x04] 0,0, "FRSTOR%S %e", -[0x06] 0,0, "FSAVE%S %e", -[0x07] 0,0, "FSTSW %e", -[0x08] 0,0, "FFREED %f", -[0x0a] 0,0, "FMOVD %f,F0", -[0x0b] 0,0, "FMOVDP %f,F0", -[0x0c] 0,0, "FUCOMD %f,F0", -[0x0d] 0,0, "FUCOMDP %f,F0", +[0x00] = { 0,0, "FMOVD %e,F0" }, +[0x02] = { 0,0, "FMOVD F0,%e" }, +[0x03] = { 0,0, "FMOVDP F0,%e" }, +[0x04] = { 0,0, "FRSTOR%S %e" }, +[0x06] = { 0,0, "FSAVE%S %e" }, +[0x07] = { 0,0, "FSTSW %e" }, +[0x08] = { 0,0, "FFREED %f" }, +[0x0a] = { 0,0, "FMOVD %f,F0" }, +[0x0b] = { 0,0, "FMOVDP %f,F0" }, +[0x0c] = { 0,0, "FUCOMD %f,F0" }, +[0x0d] = { 0,0, "FUCOMDP %f,F0" }, }; static Optable optabDE[8+8] = { -[0x00] 0,0, "FADDW %e,F0", -[0x01] 0,0, "FMULW %e,F0", -[0x02] 0,0, "FCOMW %e,F0", -[0x03] 0,0, "FCOMWP %e,F0", -[0x04] 0,0, "FSUBW %e,F0", -[0x05] 0,0, "FSUBRW %e,F0", -[0x06] 0,0, "FDIVW %e,F0", -[0x07] 0,0, "FDIVRW %e,F0", -[0x08] 0,0, "FADDDP F0,%f", -[0x09] 0,0, "FMULDP F0,%f", -[0x0b] Op_R1,0, "FCOMPDP", -[0x0c] 0,0, "FSUBRDP F0,%f", -[0x0d] 0,0, "FSUBDP F0,%f", -[0x0e] 0,0, "FDIVRDP F0,%f", -[0x0f] 0,0, "FDIVDP F0,%f", +[0x00] = { 0,0, "FADDW %e,F0" }, +[0x01] = { 0,0, "FMULW %e,F0" }, +[0x02] = { 0,0, "FCOMW %e,F0" }, +[0x03] = { 0,0, "FCOMWP %e,F0" }, +[0x04] = { 0,0, "FSUBW %e,F0" }, +[0x05] = { 0,0, "FSUBRW %e,F0" }, +[0x06] = { 0,0, "FDIVW %e,F0" }, +[0x07] = { 0,0, "FDIVRW %e,F0" }, +[0x08] = { 0,0, "FADDDP F0,%f" }, +[0x09] = { 0,0, "FMULDP F0,%f" }, +[0x0b] = { Op_R1,0, "FCOMPDP" }, +[0x0c] = { 0,0, "FSUBRDP F0,%f" }, +[0x0d] = { 0,0, "FSUBDP F0,%f" }, +[0x0e] = { 0,0, "FDIVRDP F0,%f" }, +[0x0f] = { 0,0, "FDIVDP F0,%f" }, }; static Optable optabDF[8+8] = { -[0x00] 0,0, "FMOVW %e,F0", -[0x02] 0,0, "FMOVW F0,%e", -[0x03] 0,0, "FMOVWP F0,%e", -[0x04] 0,0, "FBLD %e", -[0x05] 0,0, "FMOVL %e,F0", -[0x06] 0,0, "FBSTP %e", -[0x07] 0,0, "FMOVLP F0,%e", -[0x0c] Op_R0,0, "FSTSW %OAX", -[0x0d] 0,0, "FUCOMIP F0,%f", -[0x0e] 0,0, "FCOMIP F0,%f", +[0x00] = { 0,0, "FMOVW %e,F0" }, +[0x02] = { 0,0, "FMOVW F0,%e" }, +[0x03] = { 0,0, "FMOVWP F0,%e" }, +[0x04] = { 0,0, "FBLD %e" }, +[0x05] = { 0,0, "FMOVL %e,F0" }, +[0x06] = { 0,0, "FBSTP %e" }, +[0x07] = { 0,0, "FMOVLP F0,%e" }, +[0x0c] = { Op_R0,0, "FSTSW %OAX" }, +[0x0d] = { 0,0, "FUCOMIP F0,%f" }, +[0x0e] = { 0,0, "FCOMIP F0,%f" }, }; static Optable optabF6[8] = { -[0x00] Ib,0, "TESTB %i,%e", -[0x02] 0,0, "NOTB %e", -[0x03] 0,0, "NEGB %e", -[0x04] 0,0, "MULB AL,%e", -[0x05] 0,0, "IMULB AL,%e", -[0x06] 0,0, "DIVB AL,%e", -[0x07] 0,0, "IDIVB AL,%e", +[0x00] = { Ib,0, "TESTB %i,%e" }, +[0x02] = { 0,0, "NOTB %e" }, +[0x03] = { 0,0, "NEGB %e" }, +[0x04] = { 0,0, "MULB AL,%e" }, +[0x05] = { 0,0, "IMULB AL,%e" }, +[0x06] = { 0,0, "DIVB AL,%e" }, +[0x07] = { 0,0, "IDIVB AL,%e" }, }; static Optable optabF7[8] = { -[0x00] Iwd,0, "TEST%S %i,%e", -[0x02] 0,0, "NOT%S %e", -[0x03] 0,0, "NEG%S %e", -[0x04] 0,0, "MUL%S %OAX,%e", -[0x05] 0,0, "IMUL%S %OAX,%e", -[0x06] 0,0, "DIV%S %OAX,%e", -[0x07] 0,0, "IDIV%S %OAX,%e", +[0x00] = { Iwd,0, "TEST%S %i,%e" }, +[0x02] = { 0,0, "NOT%S %e" }, +[0x03] = { 0,0, "NEG%S %e" }, +[0x04] = { 0,0, "MUL%S %OAX,%e" }, +[0x05] = { 0,0, "IMUL%S %OAX,%e" }, +[0x06] = { 0,0, "DIV%S %OAX,%e" }, +[0x07] = { 0,0, "IDIV%S %OAX,%e" }, }; static Optable optabFE[8] = { -[0x00] 0,0, "INCB %e", -[0x01] 0,0, "DECB %e", +[0x00] = { 0,0, "INCB %e" }, +[0x01] = { 0,0, "DECB %e" }, }; static Optable optabFF[8] = { -[0x00] 0,0, "INC%S %e", -[0x01] 0,0, "DEC%S %e", -[0x02] JUMP,0, "CALL* %e", -[0x03] JUMP,0, "CALLF* %e", -[0x04] JUMP,0, "JMP* %e", -[0x05] JUMP,0, "JMPF* %e", -[0x06] 0,0, "PUSHL %e", +[0x00] = { 0,0, "INC%S %e" }, +[0x01] = { 0,0, "DEC%S %e" }, +[0x02] = { JUMP,0, "CALL* %e" }, +[0x03] = { JUMP,0, "CALLF* %e" }, +[0x04] = { JUMP,0, "JMP* %e" }, +[0x05] = { JUMP,0, "JMPF* %e" }, +[0x06] = { 0,0, "PUSHL %e" }, }; static Optable optable[256+2] = { -[0x00] RMB,0, "ADDB %r,%e", -[0x01] RM,0, "ADD%S %r,%e", -[0x02] RMB,0, "ADDB %e,%r", -[0x03] RM,0, "ADD%S %e,%r", -[0x04] Ib,0, "ADDB %i,AL", -[0x05] Iwd,0, "ADD%S %i,%OAX", -[0x06] 0,0, "PUSHL ES", -[0x07] 0,0, "POPL ES", -[0x08] RMB,0, "ORB %r,%e", -[0x09] RM,0, "OR%S %r,%e", -[0x0a] RMB,0, "ORB %e,%r", -[0x0b] RM,0, "OR%S %e,%r", -[0x0c] Ib,0, "ORB %i,AL", -[0x0d] Iwd,0, "OR%S %i,%OAX", -[0x0e] 0,0, "PUSHL CS", -[0x0f] AUXMM,0, optab0F, -[0x10] RMB,0, "ADCB %r,%e", -[0x11] RM,0, "ADC%S %r,%e", -[0x12] RMB,0, "ADCB %e,%r", -[0x13] RM,0, "ADC%S %e,%r", -[0x14] Ib,0, "ADCB %i,AL", -[0x15] Iwd,0, "ADC%S %i,%OAX", -[0x16] 0,0, "PUSHL SS", -[0x17] 0,0, "POPL SS", -[0x18] RMB,0, "SBBB %r,%e", -[0x19] RM,0, "SBB%S %r,%e", -[0x1a] RMB,0, "SBBB %e,%r", -[0x1b] RM,0, "SBB%S %e,%r", -[0x1c] Ib,0, "SBBB %i,AL", -[0x1d] Iwd,0, "SBB%S %i,%OAX", -[0x1e] 0,0, "PUSHL DS", -[0x1f] 0,0, "POPL DS", -[0x20] RMB,0, "ANDB %r,%e", -[0x21] RM,0, "AND%S %r,%e", -[0x22] RMB,0, "ANDB %e,%r", -[0x23] RM,0, "AND%S %e,%r", -[0x24] Ib,0, "ANDB %i,AL", -[0x25] Iwd,0, "AND%S %i,%OAX", -[0x26] SEG,0, "ES:", -[0x27] 0,0, "DAA", -[0x28] RMB,0, "SUBB %r,%e", -[0x29] RM,0, "SUB%S %r,%e", -[0x2a] RMB,0, "SUBB %e,%r", -[0x2b] RM,0, "SUB%S %e,%r", -[0x2c] Ib,0, "SUBB %i,AL", -[0x2d] Iwd,0, "SUB%S %i,%OAX", -[0x2e] SEG,0, "CS:", -[0x2f] 0,0, "DAS", -[0x30] RMB,0, "XORB %r,%e", -[0x31] RM,0, "XOR%S %r,%e", -[0x32] RMB,0, "XORB %e,%r", -[0x33] RM,0, "XOR%S %e,%r", -[0x34] Ib,0, "XORB %i,AL", -[0x35] Iwd,0, "XOR%S %i,%OAX", -[0x36] SEG,0, "SS:", -[0x37] 0,0, "AAA", -[0x38] RMB,0, "CMPB %r,%e", -[0x39] RM,0, "CMP%S %r,%e", -[0x3a] RMB,0, "CMPB %e,%r", -[0x3b] RM,0, "CMP%S %e,%r", -[0x3c] Ib,0, "CMPB %i,AL", -[0x3d] Iwd,0, "CMP%S %i,%OAX", -[0x3e] SEG,0, "DS:", -[0x3f] 0,0, "AAS", -[0x40] 0,0, "INC%S %OAX", -[0x41] 0,0, "INC%S %OCX", -[0x42] 0,0, "INC%S %ODX", -[0x43] 0,0, "INC%S %OBX", -[0x44] 0,0, "INC%S %OSP", -[0x45] 0,0, "INC%S %OBP", -[0x46] 0,0, "INC%S %OSI", -[0x47] 0,0, "INC%S %ODI", -[0x48] 0,0, "DEC%S %OAX", -[0x49] 0,0, "DEC%S %OCX", -[0x4a] 0,0, "DEC%S %ODX", -[0x4b] 0,0, "DEC%S %OBX", -[0x4c] 0,0, "DEC%S %OSP", -[0x4d] 0,0, "DEC%S %OBP", -[0x4e] 0,0, "DEC%S %OSI", -[0x4f] 0,0, "DEC%S %ODI", -[0x50] 0,0, "PUSH%S %OAX", -[0x51] 0,0, "PUSH%S %OCX", -[0x52] 0,0, "PUSH%S %ODX", -[0x53] 0,0, "PUSH%S %OBX", -[0x54] 0,0, "PUSH%S %OSP", -[0x55] 0,0, "PUSH%S %OBP", -[0x56] 0,0, "PUSH%S %OSI", -[0x57] 0,0, "PUSH%S %ODI", -[0x58] 0,0, "POP%S %OAX", -[0x59] 0,0, "POP%S %OCX", -[0x5a] 0,0, "POP%S %ODX", -[0x5b] 0,0, "POP%S %OBX", -[0x5c] 0,0, "POP%S %OSP", -[0x5d] 0,0, "POP%S %OBP", -[0x5e] 0,0, "POP%S %OSI", -[0x5f] 0,0, "POP%S %ODI", -[0x60] 0,0, "PUSHA%S", -[0x61] 0,0, "POPA%S", -[0x62] RMM,0, "BOUND %e,%r", -[0x63] RM,0, "ARPL %r,%e", -[0x64] SEG,0, "FS:", -[0x65] SEG,0, "GS:", -[0x66] OPOVER,0, "", -[0x67] ADDOVER,0, "", -[0x68] Iwd,0, "PUSH%S %i", -[0x69] RM,Iwd, "IMUL%S %e,%i,%r", -[0x6a] Ib,0, "PUSH%S %i", -[0x6b] RM,Ibs, "IMUL%S %e,%i,%r", -[0x6c] 0,0, "INSB DX,(%ODI)", -[0x6d] 0,0, "INS%S DX,(%ODI)", -[0x6e] 0,0, "OUTSB (%ASI),DX", -[0x6f] 0,0, "OUTS%S (%ASI),DX", -[0x70] Jbs,0, "JOS %p", -[0x71] Jbs,0, "JOC %p", -[0x72] Jbs,0, "JCS %p", -[0x73] Jbs,0, "JCC %p", -[0x74] Jbs,0, "JEQ %p", -[0x75] Jbs,0, "JNE %p", -[0x76] Jbs,0, "JLS %p", -[0x77] Jbs,0, "JHI %p", -[0x78] Jbs,0, "JMI %p", -[0x79] Jbs,0, "JPL %p", -[0x7a] Jbs,0, "JPS %p", -[0x7b] Jbs,0, "JPC %p", -[0x7c] Jbs,0, "JLT %p", -[0x7d] Jbs,0, "JGE %p", -[0x7e] Jbs,0, "JLE %p", -[0x7f] Jbs,0, "JGT %p", -[0x80] RMOPB,0, optab80, -[0x81] RMOP,0, optab81, -[0x83] RMOP,0, optab83, -[0x84] RMB,0, "TESTB %r,%e", -[0x85] RM,0, "TEST%S %r,%e", -[0x86] RMB,0, "XCHGB %r,%e", -[0x87] RM,0, "XCHG%S %r,%e", -[0x88] RMB,0, "MOVB %r,%e", -[0x89] RM,0, "MOV%S %r,%e", -[0x8a] RMB,0, "MOVB %e,%r", -[0x8b] RM,0, "MOV%S %e,%r", -[0x8c] RM,0, "MOVW %g,%e", -[0x8d] RM,0, "LEA%S %e,%r", -[0x8e] RM,0, "MOVW %e,%g", -[0x8f] RM,0, "POP%S %e", -[0x90] 0,0, "NOP", -[0x91] 0,0, "XCHG %OCX,%OAX", -[0x92] 0,0, "XCHG %ODX,%OAX", -[0x93] 0,0, "XCHG %OBX,%OAX", -[0x94] 0,0, "XCHG %OSP,%OAX", -[0x95] 0,0, "XCHG %OBP,%OAX", -[0x96] 0,0, "XCHG %OSI,%OAX", -[0x97] 0,0, "XCHG %ODI,%OAX", -[0x98] 0,0, "%W", /* miserable CBW or CWDE */ -[0x99] 0,0, "%w", /* idiotic CWD or CDQ */ -[0x9a] PTR,0, "CALL%S %d", -[0x9b] 0,0, "WAIT", -[0x9c] 0,0, "PUSHF", -[0x9d] 0,0, "POPF", -[0x9e] 0,0, "SAHF", -[0x9f] 0,0, "LAHF", -[0xa0] Awd,0, "MOVB %i,AL", -[0xa1] Awd,0, "MOV%S %i,%OAX", -[0xa2] Awd,0, "MOVB AL,%i", -[0xa3] Awd,0, "MOV%S %OAX,%i", -[0xa4] 0,0, "MOVSB (%ASI),(%ADI)", -[0xa5] 0,0, "MOVS%S (%ASI),(%ADI)", -[0xa6] 0,0, "CMPSB (%ASI),(%ADI)", -[0xa7] 0,0, "CMPS%S (%ASI),(%ADI)", -[0xa8] Ib,0, "TESTB %i,AL", -[0xa9] Iwd,0, "TEST%S %i,%OAX", -[0xaa] 0,0, "STOSB AL,(%ADI)", -[0xab] 0,0, "STOS%S %OAX,(%ADI)", -[0xac] 0,0, "LODSB (%ASI),AL", -[0xad] 0,0, "LODS%S (%ASI),%OAX", -[0xae] 0,0, "SCASB (%ADI),AL", -[0xaf] 0,0, "SCAS%S (%ADI),%OAX", -[0xb0] Ib,0, "MOVB %i,AL", -[0xb1] Ib,0, "MOVB %i,CL", -[0xb2] Ib,0, "MOVB %i,DL", -[0xb3] Ib,0, "MOVB %i,BL", -[0xb4] Ib,0, "MOVB %i,AH", -[0xb5] Ib,0, "MOVB %i,CH", -[0xb6] Ib,0, "MOVB %i,DH", -[0xb7] Ib,0, "MOVB %i,BH", -[0xb8] Iwdq,0, "MOV%S %i,%OAX", -[0xb9] Iwdq,0, "MOV%S %i,%OCX", -[0xba] Iwdq,0, "MOV%S %i,%ODX", -[0xbb] Iwdq,0, "MOV%S %i,%OBX", -[0xbc] Iwdq,0, "MOV%S %i,%OSP", -[0xbd] Iwdq,0, "MOV%S %i,%OBP", -[0xbe] Iwdq,0, "MOV%S %i,%OSI", -[0xbf] Iwdq,0, "MOV%S %i,%ODI", -[0xc0] RMOPB,0, optabC0, -[0xc1] RMOP,0, optabC1, -[0xc2] Iw,0, "RET %i", -[0xc3] RET,0, "RET", -[0xc4] RM,0, "LES %e,%r", -[0xc5] RM,0, "LDS %e,%r", -[0xc6] RMB,Ib, "MOVB %i,%e", -[0xc7] RM,Iwd, "MOV%S %i,%e", -[0xc8] Iw2,Ib, "ENTER %i,%I", /* loony ENTER */ -[0xc9] RET,0, "LEAVE", /* bizarre LEAVE */ -[0xca] Iw,0, "RETF %i", -[0xcb] RET,0, "RETF", -[0xcc] 0,0, "INT 3", -[0xcd] Ib,0, "INTB %i", -[0xce] 0,0, "INTO", -[0xcf] 0,0, "IRET", -[0xd0] RMOPB,0, optabD0, -[0xd1] RMOP,0, optabD1, -[0xd2] RMOPB,0, optabD2, -[0xd3] RMOP,0, optabD3, -[0xd4] OA,0, "AAM", -[0xd5] OA,0, "AAD", -[0xd7] 0,0, "XLAT", -[0xd8] FRMOP,0, optabD8, -[0xd9] FRMEX,0, optabD9, -[0xda] FRMOP,0, optabDA, -[0xdb] FRMEX,0, optabDB, -[0xdc] FRMOP,0, optabDC, -[0xdd] FRMOP,0, optabDD, -[0xde] FRMOP,0, optabDE, -[0xdf] FRMOP,0, optabDF, -[0xe0] Jbs,0, "LOOPNE %p", -[0xe1] Jbs,0, "LOOPE %p", -[0xe2] Jbs,0, "LOOP %p", -[0xe3] Jbs,0, "JCXZ %p", -[0xe4] Ib,0, "INB %i,AL", -[0xe5] Ib,0, "IN%S %i,%OAX", -[0xe6] Ib,0, "OUTB AL,%i", -[0xe7] Ib,0, "OUT%S %OAX,%i", -[0xe8] Iwds,0, "CALL %p", -[0xe9] Iwds,0, "JMP %p", -[0xea] PTR,0, "JMP %d", -[0xeb] Jbs,0, "JMP %p", -[0xec] 0,0, "INB DX,AL", -[0xed] 0,0, "IN%S DX,%OAX", -[0xee] 0,0, "OUTB AL,DX", -[0xef] 0,0, "OUT%S %OAX,DX", -[0xf0] PRE,0, "LOCK", -[0xf2] OPRE,0, "REPNE", -[0xf3] OPRE,0, "REP", -[0xf4] 0,0, "HLT", -[0xf5] 0,0, "CMC", -[0xf6] RMOPB,0, optabF6, -[0xf7] RMOP,0, optabF7, -[0xf8] 0,0, "CLC", -[0xf9] 0,0, "STC", -[0xfa] 0,0, "CLI", -[0xfb] 0,0, "STI", -[0xfc] 0,0, "CLD", -[0xfd] 0,0, "STD", -[0xfe] RMOPB,0, optabFE, -[0xff] RMOP,0, optabFF, -[0x100] RM,0, "MOVLQSX %e,%r", -[0x101] RM,0, "MOVLQZX %e,%r", +[0x00] = { RMB,0, "ADDB %r,%e" }, +[0x01] = { RM,0, "ADD%S %r,%e" }, +[0x02] = { RMB,0, "ADDB %e,%r" }, +[0x03] = { RM,0, "ADD%S %e,%r" }, +[0x04] = { Ib,0, "ADDB %i,AL" }, +[0x05] = { Iwd,0, "ADD%S %i,%OAX" }, +[0x06] = { 0,0, "PUSHL ES" }, +[0x07] = { 0,0, "POPL ES" }, +[0x08] = { RMB,0, "ORB %r,%e" }, +[0x09] = { RM,0, "OR%S %r,%e" }, +[0x0a] = { RMB,0, "ORB %e,%r" }, +[0x0b] = { RM,0, "OR%S %e,%r" }, +[0x0c] = { Ib,0, "ORB %i,AL" }, +[0x0d] = { Iwd,0, "OR%S %i,%OAX" }, +[0x0e] = { 0,0, "PUSHL CS" }, +[0x0f] = { AUXMM,0, optab0F }, +[0x10] = { RMB,0, "ADCB %r,%e" }, +[0x11] = { RM,0, "ADC%S %r,%e" }, +[0x12] = { RMB,0, "ADCB %e,%r" }, +[0x13] = { RM,0, "ADC%S %e,%r" }, +[0x14] = { Ib,0, "ADCB %i,AL" }, +[0x15] = { Iwd,0, "ADC%S %i,%OAX" }, +[0x16] = { 0,0, "PUSHL SS" }, +[0x17] = { 0,0, "POPL SS" }, +[0x18] = { RMB,0, "SBBB %r,%e" }, +[0x19] = { RM,0, "SBB%S %r,%e" }, +[0x1a] = { RMB,0, "SBBB %e,%r" }, +[0x1b] = { RM,0, "SBB%S %e,%r" }, +[0x1c] = { Ib,0, "SBBB %i,AL" }, +[0x1d] = { Iwd,0, "SBB%S %i,%OAX" }, +[0x1e] = { 0,0, "PUSHL DS" }, +[0x1f] = { 0,0, "POPL DS" }, +[0x20] = { RMB,0, "ANDB %r,%e" }, +[0x21] = { RM,0, "AND%S %r,%e" }, +[0x22] = { RMB,0, "ANDB %e,%r" }, +[0x23] = { RM,0, "AND%S %e,%r" }, +[0x24] = { Ib,0, "ANDB %i,AL" }, +[0x25] = { Iwd,0, "AND%S %i,%OAX" }, +[0x26] = { SEG,0, "ES:" }, +[0x27] = { 0,0, "DAA" }, +[0x28] = { RMB,0, "SUBB %r,%e" }, +[0x29] = { RM,0, "SUB%S %r,%e" }, +[0x2a] = { RMB,0, "SUBB %e,%r" }, +[0x2b] = { RM,0, "SUB%S %e,%r" }, +[0x2c] = { Ib,0, "SUBB %i,AL" }, +[0x2d] = { Iwd,0, "SUB%S %i,%OAX" }, +[0x2e] = { SEG,0, "CS:" }, +[0x2f] = { 0,0, "DAS" }, +[0x30] = { RMB,0, "XORB %r,%e" }, +[0x31] = { RM,0, "XOR%S %r,%e" }, +[0x32] = { RMB,0, "XORB %e,%r" }, +[0x33] = { RM,0, "XOR%S %e,%r" }, +[0x34] = { Ib,0, "XORB %i,AL" }, +[0x35] = { Iwd,0, "XOR%S %i,%OAX" }, +[0x36] = { SEG,0, "SS:" }, +[0x37] = { 0,0, "AAA" }, +[0x38] = { RMB,0, "CMPB %r,%e" }, +[0x39] = { RM,0, "CMP%S %r,%e" }, +[0x3a] = { RMB,0, "CMPB %e,%r" }, +[0x3b] = { RM,0, "CMP%S %e,%r" }, +[0x3c] = { Ib,0, "CMPB %i,AL" }, +[0x3d] = { Iwd,0, "CMP%S %i,%OAX" }, +[0x3e] = { SEG,0, "DS:" }, +[0x3f] = { 0,0, "AAS" }, +[0x40] = { 0,0, "INC%S %OAX" }, +[0x41] = { 0,0, "INC%S %OCX" }, +[0x42] = { 0,0, "INC%S %ODX" }, +[0x43] = { 0,0, "INC%S %OBX" }, +[0x44] = { 0,0, "INC%S %OSP" }, +[0x45] = { 0,0, "INC%S %OBP" }, +[0x46] = { 0,0, "INC%S %OSI" }, +[0x47] = { 0,0, "INC%S %ODI" }, +[0x48] = { 0,0, "DEC%S %OAX" }, +[0x49] = { 0,0, "DEC%S %OCX" }, +[0x4a] = { 0,0, "DEC%S %ODX" }, +[0x4b] = { 0,0, "DEC%S %OBX" }, +[0x4c] = { 0,0, "DEC%S %OSP" }, +[0x4d] = { 0,0, "DEC%S %OBP" }, +[0x4e] = { 0,0, "DEC%S %OSI" }, +[0x4f] = { 0,0, "DEC%S %ODI" }, +[0x50] = { 0,0, "PUSH%S %OAX" }, +[0x51] = { 0,0, "PUSH%S %OCX" }, +[0x52] = { 0,0, "PUSH%S %ODX" }, +[0x53] = { 0,0, "PUSH%S %OBX" }, +[0x54] = { 0,0, "PUSH%S %OSP" }, +[0x55] = { 0,0, "PUSH%S %OBP" }, +[0x56] = { 0,0, "PUSH%S %OSI" }, +[0x57] = { 0,0, "PUSH%S %ODI" }, +[0x58] = { 0,0, "POP%S %OAX" }, +[0x59] = { 0,0, "POP%S %OCX" }, +[0x5a] = { 0,0, "POP%S %ODX" }, +[0x5b] = { 0,0, "POP%S %OBX" }, +[0x5c] = { 0,0, "POP%S %OSP" }, +[0x5d] = { 0,0, "POP%S %OBP" }, +[0x5e] = { 0,0, "POP%S %OSI" }, +[0x5f] = { 0,0, "POP%S %ODI" }, +[0x60] = { 0,0, "PUSHA%S" }, +[0x61] = { 0,0, "POPA%S" }, +[0x62] = { RMM,0, "BOUND %e,%r" }, +[0x63] = { RM,0, "ARPL %r,%e" }, +[0x64] = { SEG,0, "FS:" }, +[0x65] = { SEG,0, "GS:" }, +[0x66] = { OPOVER,0, "" }, +[0x67] = { ADDOVER,0, "" }, +[0x68] = { Iwd,0, "PUSH%S %i" }, +[0x69] = { RM,Iwd, "IMUL%S %e,%i,%r" }, +[0x6a] = { Ib,0, "PUSH%S %i" }, +[0x6b] = { RM,Ibs, "IMUL%S %e,%i,%r" }, +[0x6c] = { 0,0, "INSB DX,(%ODI)" }, +[0x6d] = { 0,0, "INS%S DX,(%ODI)" }, +[0x6e] = { 0,0, "OUTSB (%ASI),DX" }, +[0x6f] = { 0,0, "OUTS%S (%ASI),DX" }, +[0x70] = { Jbs,0, "JOS %p" }, +[0x71] = { Jbs,0, "JOC %p" }, +[0x72] = { Jbs,0, "JCS %p" }, +[0x73] = { Jbs,0, "JCC %p" }, +[0x74] = { Jbs,0, "JEQ %p" }, +[0x75] = { Jbs,0, "JNE %p" }, +[0x76] = { Jbs,0, "JLS %p" }, +[0x77] = { Jbs,0, "JHI %p" }, +[0x78] = { Jbs,0, "JMI %p" }, +[0x79] = { Jbs,0, "JPL %p" }, +[0x7a] = { Jbs,0, "JPS %p" }, +[0x7b] = { Jbs,0, "JPC %p" }, +[0x7c] = { Jbs,0, "JLT %p" }, +[0x7d] = { Jbs,0, "JGE %p" }, +[0x7e] = { Jbs,0, "JLE %p" }, +[0x7f] = { Jbs,0, "JGT %p" }, +[0x80] = { RMOPB,0, optab80 }, +[0x81] = { RMOP,0, optab81 }, +[0x83] = { RMOP,0, optab83 }, +[0x84] = { RMB,0, "TESTB %r,%e" }, +[0x85] = { RM,0, "TEST%S %r,%e" }, +[0x86] = { RMB,0, "XCHGB %r,%e" }, +[0x87] = { RM,0, "XCHG%S %r,%e" }, +[0x88] = { RMB,0, "MOVB %r,%e" }, +[0x89] = { RM,0, "MOV%S %r,%e" }, +[0x8a] = { RMB,0, "MOVB %e,%r" }, +[0x8b] = { RM,0, "MOV%S %e,%r" }, +[0x8c] = { RM,0, "MOVW %g,%e" }, +[0x8d] = { RM,0, "LEA%S %e,%r" }, +[0x8e] = { RM,0, "MOVW %e,%g" }, +[0x8f] = { RM,0, "POP%S %e" }, +[0x90] = { 0,0, "NOP" }, +[0x91] = { 0,0, "XCHG %OCX,%OAX" }, +[0x92] = { 0,0, "XCHG %ODX,%OAX" }, +[0x93] = { 0,0, "XCHG %OBX,%OAX" }, +[0x94] = { 0,0, "XCHG %OSP,%OAX" }, +[0x95] = { 0,0, "XCHG %OBP,%OAX" }, +[0x96] = { 0,0, "XCHG %OSI,%OAX" }, +[0x97] = { 0,0, "XCHG %ODI,%OAX" }, +[0x98] = { 0,0, "%W" }, /* miserable CBW or CWDE */ +[0x99] = { 0,0, "%w" }, /* idiotic CWD or CDQ */ +[0x9a] = { PTR,0, "CALL%S %d" }, +[0x9b] = { 0,0, "WAIT" }, +[0x9c] = { 0,0, "PUSHF" }, +[0x9d] = { 0,0, "POPF" }, +[0x9e] = { 0,0, "SAHF" }, +[0x9f] = { 0,0, "LAHF" }, +[0xa0] = { Awd,0, "MOVB %i,AL" }, +[0xa1] = { Awd,0, "MOV%S %i,%OAX" }, +[0xa2] = { Awd,0, "MOVB AL,%i" }, +[0xa3] = { Awd,0, "MOV%S %OAX,%i" }, +[0xa4] = { 0,0, "MOVSB (%ASI),(%ADI)" }, +[0xa5] = { 0,0, "MOVS%S (%ASI),(%ADI)" }, +[0xa6] = { 0,0, "CMPSB (%ASI),(%ADI)" }, +[0xa7] = { 0,0, "CMPS%S (%ASI),(%ADI)" }, +[0xa8] = { Ib,0, "TESTB %i,AL" }, +[0xa9] = { Iwd,0, "TEST%S %i,%OAX" }, +[0xaa] = { 0,0, "STOSB AL,(%ADI)" }, +[0xab] = { 0,0, "STOS%S %OAX,(%ADI)" }, +[0xac] = { 0,0, "LODSB (%ASI),AL" }, +[0xad] = { 0,0, "LODS%S (%ASI),%OAX" }, +[0xae] = { 0,0, "SCASB (%ADI),AL" }, +[0xaf] = { 0,0, "SCAS%S (%ADI),%OAX" }, +[0xb0] = { Ib,0, "MOVB %i,AL" }, +[0xb1] = { Ib,0, "MOVB %i,CL" }, +[0xb2] = { Ib,0, "MOVB %i,DL" }, +[0xb3] = { Ib,0, "MOVB %i,BL" }, +[0xb4] = { Ib,0, "MOVB %i,AH" }, +[0xb5] = { Ib,0, "MOVB %i,CH" }, +[0xb6] = { Ib,0, "MOVB %i,DH" }, +[0xb7] = { Ib,0, "MOVB %i,BH" }, +[0xb8] = { Iwdq,0, "MOV%S %i,%OAX" }, +[0xb9] = { Iwdq,0, "MOV%S %i,%OCX" }, +[0xba] = { Iwdq,0, "MOV%S %i,%ODX" }, +[0xbb] = { Iwdq,0, "MOV%S %i,%OBX" }, +[0xbc] = { Iwdq,0, "MOV%S %i,%OSP" }, +[0xbd] = { Iwdq,0, "MOV%S %i,%OBP" }, +[0xbe] = { Iwdq,0, "MOV%S %i,%OSI" }, +[0xbf] = { Iwdq,0, "MOV%S %i,%ODI" }, +[0xc0] = { RMOPB,0, optabC0 }, +[0xc1] = { RMOP,0, optabC1 }, +[0xc2] = { Iw,0, "RET %i" }, +[0xc3] = { RET,0, "RET" }, +[0xc4] = { RM,0, "LES %e,%r" }, +[0xc5] = { RM,0, "LDS %e,%r" }, +[0xc6] = { RMB,Ib, "MOVB %i,%e" }, +[0xc7] = { RM,Iwd, "MOV%S %i,%e" }, +[0xc8] = { Iw2,Ib, "ENTER %i,%I" }, /* loony ENTER */ +[0xc9] = { RET,0, "LEAVE" }, /* bizarre LEAVE */ +[0xca] = { Iw,0, "RETF %i" }, +[0xcb] = { RET,0, "RETF" }, +[0xcc] = { 0,0, "INT 3" }, +[0xcd] = { Ib,0, "INTB %i" }, +[0xce] = { 0,0, "INTO" }, +[0xcf] = { 0,0, "IRET" }, +[0xd0] = { RMOPB,0, optabD0 }, +[0xd1] = { RMOP,0, optabD1 }, +[0xd2] = { RMOPB,0, optabD2 }, +[0xd3] = { RMOP,0, optabD3 }, +[0xd4] = { OA,0, "AAM" }, +[0xd5] = { OA,0, "AAD" }, +[0xd7] = { 0,0, "XLAT" }, +[0xd8] = { FRMOP,0, optabD8 }, +[0xd9] = { FRMEX,0, optabD9 }, +[0xda] = { FRMOP,0, optabDA }, +[0xdb] = { FRMEX,0, optabDB }, +[0xdc] = { FRMOP,0, optabDC }, +[0xdd] = { FRMOP,0, optabDD }, +[0xde] = { FRMOP,0, optabDE }, +[0xdf] = { FRMOP,0, optabDF }, +[0xe0] = { Jbs,0, "LOOPNE %p" }, +[0xe1] = { Jbs,0, "LOOPE %p" }, +[0xe2] = { Jbs,0, "LOOP %p" }, +[0xe3] = { Jbs,0, "JCXZ %p" }, +[0xe4] = { Ib,0, "INB %i,AL" }, +[0xe5] = { Ib,0, "IN%S %i,%OAX" }, +[0xe6] = { Ib,0, "OUTB AL,%i" }, +[0xe7] = { Ib,0, "OUT%S %OAX,%i" }, +[0xe8] = { Iwds,0, "CALL %p" }, +[0xe9] = { Iwds,0, "JMP %p" }, +[0xea] = { PTR,0, "JMP %d" }, +[0xeb] = { Jbs,0, "JMP %p" }, +[0xec] = { 0,0, "INB DX,AL" }, +[0xed] = { 0,0, "IN%S DX,%OAX" }, +[0xee] = { 0,0, "OUTB AL,DX" }, +[0xef] = { 0,0, "OUT%S %OAX,DX" }, +[0xf0] = { PRE,0, "LOCK" }, +[0xf2] = { OPRE,0, "REPNE" }, +[0xf3] = { OPRE,0, "REP" }, +[0xf4] = { 0,0, "HLT" }, +[0xf5] = { 0,0, "CMC" }, +[0xf6] = { RMOPB,0, optabF6 }, +[0xf7] = { RMOP,0, optabF7 }, +[0xf8] = { 0,0, "CLC" }, +[0xf9] = { 0,0, "STC" }, +[0xfa] = { 0,0, "CLI" }, +[0xfb] = { 0,0, "STI" }, +[0xfc] = { 0,0, "CLD" }, +[0xfd] = { 0,0, "STD" }, +[0xfe] = { RMOPB,0, optabFE }, +[0xff] = { RMOP,0, optabFF }, +[0x100] = { RM,0, "MOVLQSX %e,%r" }, +[0x101] = { RM,0, "MOVLQZX %e,%r" }, }; /* @@ -1894,24 +1894,24 @@ bprint(Instr *ip, char *fmt, ...) #define ONAME(ip) "" static char *reg[] = { -[AX] "AX", -[CX] "CX", -[DX] "DX", -[BX] "BX", -[SP] "SP", -[BP] "BP", -[SI] "SI", -[DI] "DI", +[AX] = "AX", +[CX] = "CX", +[DX] = "DX", +[BX] = "BX", +[SP] = "SP", +[BP] = "BP", +[SI] = "SI", +[DI] = "DI", /* amd64 */ -[AMD64_R8] "R8", -[AMD64_R9] "R9", -[AMD64_R10] "R10", -[AMD64_R11] "R11", -[AMD64_R12] "R12", -[AMD64_R13] "R13", -[AMD64_R14] "R14", -[AMD64_R15] "R15", +[AMD64_R8] = "R8", +[AMD64_R9] = "R9", +[AMD64_R10] = "R10", +[AMD64_R11] = "R11", +[AMD64_R12] = "R12", +[AMD64_R13] = "R13", +[AMD64_R14] = "R14", +[AMD64_R15] = "R15", }; static char *breg[] = { "AL", "CL", "DL", "BL", "AH", "CH", "DH", "BH" }; diff --git a/src/libmach/obj.c b/src/libmach/obj.c index 7d660787b..7999f24c6 100644 --- a/src/libmach/obj.c +++ b/src/libmach/obj.c @@ -81,17 +81,17 @@ struct Obj /* functions to handle each intermediate (.$O) file */ static Obj obj[] = { /* functions to identify and parse each type of obj */ - [Obj68020] "68020 .2", _is2, _read2, - [ObjAmd64] "amd64 .6", _is6, _read6, - [ObjArm] "arm .5", _is5, _read5, - [ObjAlpha] "alpha .7", _is7, _read7, - [Obj386] "386 .8", _is8, _read8, - [ObjSparc] "sparc .k", _isk, _readk, - [ObjPower] "power .q", _isq, _readq, - [ObjMips] "mips .v", _isv, _readv, - [ObjSparc64] "sparc64 .u", _isu, _readu, - [ObjPower64] "power64 .9", _is9, _read9, - [Maxobjtype] 0, 0 + [Obj68020] = { "68020 .2", _is2, _read2 }, + [ObjAmd64] = { "amd64 .6", _is6 , _read6 }, + [ObjArm] = { "arm .5", _is5, _read5 }, + [ObjAlpha] = { "alpha .7", _is7, _read7 }, + [Obj386] = { "386 .8", _is8, _read8 }, + [ObjSparc] = { "sparc .k", _isk, _readk }, + [ObjPower] = { "power .q", _isq, _readq }, + [ObjMips] = { "mips .v", _isv, _readv }, + [ObjSparc64] = { "sparc64 .u", _isu, _readu }, + [ObjPower64] = { "power64 .9", _is9, _read9 }, + [Maxobjtype] = { 0, 0, 0 } }; struct Symtab diff --git a/src/pkg/Makefile b/src/pkg/Makefile index fc5548e98..2d6b3d014 100644 --- a/src/pkg/Makefile +++ b/src/pkg/Makefile @@ -59,7 +59,7 @@ DIRS=\ crypto/tls\ crypto/twofish\ crypto/x509\ - crypto/x509/crl\ + crypto/x509/pkix\ crypto/xtea\ debug/dwarf\ debug/macho\ @@ -77,13 +77,14 @@ DIRS=\ encoding/pem\ exec\ exp/datafmt\ - exp/draw\ - exp/draw/x11\ exp/eval\ + exp/gui\ + exp/gui/x11\ expvar\ flag\ fmt\ go/ast\ + go/build\ go/doc\ go/parser\ go/printer\ @@ -106,6 +107,7 @@ DIRS=\ http/spdy\ image\ image/bmp\ + image/draw\ image/gif\ image/jpeg\ image/png\ @@ -182,8 +184,10 @@ endif NOTEST+=\ crypto\ crypto/openpgp/error\ + crypto/x509/pkix\ debug/proc\ - exp/draw/x11\ + exp/gui\ + exp/gui/x11\ go/ast\ go/doc\ go/token\ diff --git a/src/pkg/big/int.go b/src/pkg/big/int.go index e66c34a83..22bdf8d2f 100755 --- a/src/pkg/big/int.go +++ b/src/pkg/big/int.go @@ -816,13 +816,13 @@ func (z *Int) Not(x *Int) *Int { // Gob codec version. Permits backward-compatible changes to the encoding. -const version byte = 1 +const intGobVersion byte = 1 // GobEncode implements the gob.GobEncoder interface. func (z *Int) GobEncode() ([]byte, os.Error) { - buf := make([]byte, len(z.abs)*_S+1) // extra byte for version and sign bit + buf := make([]byte, 1+len(z.abs)*_S) // extra byte for version and sign bit i := z.abs.bytes(buf) - 1 // i >= 0 - b := version << 1 // make space for sign bit + b := intGobVersion << 1 // make space for sign bit if z.neg { b |= 1 } @@ -837,7 +837,7 @@ func (z *Int) GobDecode(buf []byte) os.Error { return os.ErrorString("Int.GobDecode: no data") } b := buf[0] - if b>>1 != version { + if b>>1 != intGobVersion { return os.ErrorString(fmt.Sprintf("Int.GobDecode: encoding version %d not supported", b>>1)) } z.neg = b&1 != 0 diff --git a/src/pkg/big/int_test.go b/src/pkg/big/int_test.go index 1a492925b..58a55030d 100755 --- a/src/pkg/big/int_test.go +++ b/src/pkg/big/int_test.go @@ -1303,6 +1303,7 @@ func TestModInverse(t *testing.T) { } +// used by TestIntGobEncoding and TestRatGobEncoding var gobEncodingTests = []string{ "0", "1", @@ -1313,7 +1314,7 @@ var gobEncodingTests = []string{ "298472983472983471903246121093472394872319615612417471234712061", } -func TestGobEncoding(t *testing.T) { +func TestIntGobEncoding(t *testing.T) { var medium bytes.Buffer enc := gob.NewEncoder(&medium) dec := gob.NewDecoder(&medium) @@ -1321,7 +1322,8 @@ func TestGobEncoding(t *testing.T) { for j := 0; j < 2; j++ { medium.Reset() // empty buffer for each test case (in case of failures) stest := test - if j == 0 { + if j != 0 { + // negative numbers stest = "-" + test } var tx Int diff --git a/src/pkg/big/nat.go b/src/pkg/big/nat.go index fa09d6531..734568e06 100755 --- a/src/pkg/big/nat.go +++ b/src/pkg/big/nat.go @@ -615,6 +615,7 @@ func (x nat) bitLen() int { // MaxBase is the largest number base accepted for string conversions. const MaxBase = 'z' - 'a' + 10 + 1 // = hexValue('z') + 1 + func hexValue(ch int) Word { d := MaxBase + 1 // illegal base switch { @@ -733,46 +734,136 @@ const ( uppercaseDigits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" ) + +// decimalString returns a decimal representation of x. +// It calls x.string with the charset "0123456789". +func (x nat) decimalString() string { + return x.string(lowercaseDigits[0:10]) +} + + // string converts x to a string using digits from a charset; a digit with // value d is represented by charset[d]. The conversion base is determined // by len(charset), which must be >= 2. func (x nat) string(charset string) string { - base := len(charset) + b := Word(len(charset)) // special cases switch { - case base < 2: + case b < 2 || b > 256: panic("illegal base") case len(x) == 0: return string(charset[0]) } // allocate buffer for conversion - i := x.bitLen()/log2(Word(base)) + 1 // +1: round up + i := x.bitLen()/log2(b) + 1 // +1: round up s := make([]byte, i) - // don't destroy x + // special case: power of two bases can avoid divisions completely + if b == b&-b { + // shift is base-b digit size in bits + shift := uint(trailingZeroBits(b)) // shift > 0 because b >= 2 + mask := Word(1)<<shift - 1 + w := x[0] + nbits := uint(_W) // number of unprocessed bits in w + + // convert less-significant words + for k := 1; k < len(x); k++ { + // convert full digits + for nbits >= shift { + i-- + s[i] = charset[w&mask] + w >>= shift + nbits -= shift + } + + // convert any partial leading digit and advance to next word + if nbits == 0 { + // no partial digit remaining, just advance + w = x[k] + nbits = _W + } else { + // partial digit in current (k-1) and next (k) word + w |= x[k] << nbits + i-- + s[i] = charset[w&mask] + + // advance + w = x[k] >> (shift - nbits) + nbits = _W - (shift - nbits) + } + } + + // convert digits of most-significant word (omit leading zeros) + for nbits >= 0 && w != 0 { + i-- + s[i] = charset[w&mask] + w >>= shift + nbits -= shift + } + + return string(s[i:]) + } + + // general case: extract groups of digits by multiprecision division + + // maximize ndigits where b**ndigits < 2^_W; bb (big base) is b**ndigits + bb := Word(1) + ndigits := 0 + for max := Word(_M / b); bb <= max; bb *= b { + ndigits++ + } + + // preserve x, create local copy for use in repeated divisions q := nat(nil).set(x) + var r Word // convert - for len(q) > 0 { - i-- - var r Word - q, r = q.divW(q, Word(base)) - s[i] = charset[r] + if b == 10 { // hard-coding for 10 here speeds this up by 1.25x + for len(q) > 0 { + // extract least significant, base bb "digit" + q, r = q.divW(q, bb) // N.B. >82% of time is here. Optimize divW + if len(q) == 0 { + // skip leading zeros in most-significant group of digits + for j := 0; j < ndigits && r != 0; j++ { + i-- + s[i] = charset[r%10] + r /= 10 + } + } else { + for j := 0; j < ndigits; j++ { + i-- + s[i] = charset[r%10] + r /= 10 + } + } + } + } else { + for len(q) > 0 { + // extract least significant group of digits + q, r = q.divW(q, bb) // N.B. >82% of time is here. Optimize divW + if len(q) == 0 { + // skip leading zeros in most-significant group of digits + for j := 0; j < ndigits && r != 0; j++ { + i-- + s[i] = charset[r%b] + r /= b + } + } else { + for j := 0; j < ndigits; j++ { + i-- + s[i] = charset[r%b] + r /= b + } + } + } } return string(s[i:]) } -// decimalString returns a decimal representation of x. -// It calls x.string with the charset "0123456789". -func (x nat) decimalString() string { - return x.string(lowercaseDigits[0:10]) -} - - const deBruijn32 = 0x077CB531 var deBruijn32Lookup = []byte{ @@ -789,6 +880,7 @@ var deBruijn64Lookup = []byte{ 54, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9, 13, 8, 7, 6, } + // trailingZeroBits returns the number of consecutive zero bits on the right // side of the given Word. // See Knuth, volume 4, section 7.3.1 @@ -960,7 +1052,9 @@ func (z nat) xor(x, y nat) nat { // greaterThan returns true iff (x1<<_W + x2) > (y1<<_W + y2) -func greaterThan(x1, x2, y1, y2 Word) bool { return x1 > y1 || x1 == y1 && x2 > y2 } +func greaterThan(x1, x2, y1, y2 Word) bool { + return x1 > y1 || x1 == y1 && x2 > y2 +} // modW returns x % d. diff --git a/src/pkg/big/nat_test.go b/src/pkg/big/nat_test.go index 50ea469be..fd93592dd 100755 --- a/src/pkg/big/nat_test.go +++ b/src/pkg/big/nat_test.go @@ -5,6 +5,7 @@ package big import ( + "fmt" "os" "strings" "testing" @@ -171,6 +172,36 @@ func BenchmarkMul(b *testing.B) { } +func toString(x nat, charset string) string { + base := len(charset) + + // special cases + switch { + case base < 2: + panic("illegal base") + case len(x) == 0: + return string(charset[0]) + } + + // allocate buffer for conversion + i := x.bitLen()/log2(Word(base)) + 1 // +1: round up + s := make([]byte, i) + + // don't destroy x + q := nat(nil).set(x) + + // convert + for len(q) > 0 { + i-- + var r Word + q, r = q.divW(q, Word(base)) + s[i] = charset[r] + } + + return string(s[i:]) +} + + var strTests = []struct { x nat // nat value to be converted c string // conversion charset @@ -360,6 +391,187 @@ func BenchmarkScanPi(b *testing.B) { } +const ( + // 314**271 + // base 2: 2249 digits + // base 8: 751 digits + // base 10: 678 digits + // base 16: 563 digits + shortBase = 314 + shortExponent = 271 + + // 3141**2178 + // base 2: 31577 digits + // base 8: 10527 digits + // base 10: 9507 digits + // base 16: 7895 digits + mediumBase = 3141 + mediumExponent = 2718 + + // 3141**2178 + // base 2: 406078 digits + // base 8: 135360 digits + // base 10: 122243 digits + // base 16: 101521 digits + longBase = 31415 + longExponent = 27182 +) + + +func BenchmarkScanShort2(b *testing.B) { + ScanHelper(b, 2, shortBase, shortExponent) +} + + +func BenchmarkScanShort8(b *testing.B) { + ScanHelper(b, 8, shortBase, shortExponent) +} + + +func BenchmarkScanSort10(b *testing.B) { + ScanHelper(b, 10, shortBase, shortExponent) +} + + +func BenchmarkScanShort16(b *testing.B) { + ScanHelper(b, 16, shortBase, shortExponent) +} + + +func BenchmarkScanMedium2(b *testing.B) { + ScanHelper(b, 2, mediumBase, mediumExponent) +} + + +func BenchmarkScanMedium8(b *testing.B) { + ScanHelper(b, 8, mediumBase, mediumExponent) +} + + +func BenchmarkScanMedium10(b *testing.B) { + ScanHelper(b, 10, mediumBase, mediumExponent) +} + + +func BenchmarkScanMedium16(b *testing.B) { + ScanHelper(b, 16, mediumBase, mediumExponent) +} + + +func BenchmarkScanLong2(b *testing.B) { + ScanHelper(b, 2, longBase, longExponent) +} + + +func BenchmarkScanLong8(b *testing.B) { + ScanHelper(b, 8, longBase, longExponent) +} + + +func BenchmarkScanLong10(b *testing.B) { + ScanHelper(b, 10, longBase, longExponent) +} + + +func BenchmarkScanLong16(b *testing.B) { + ScanHelper(b, 16, longBase, longExponent) +} + + +func ScanHelper(b *testing.B, base int, xv, yv Word) { + b.StopTimer() + var x, y, z nat + x = x.setWord(xv) + y = y.setWord(yv) + z = z.expNN(x, y, nil) + + var s string + s = z.string(lowercaseDigits[0:base]) + if t := toString(z, lowercaseDigits[0:base]); t != s { + panic(fmt.Sprintf("scanning: got %s; want %s", s, t)) + } + b.StartTimer() + + for i := 0; i < b.N; i++ { + x.scan(strings.NewReader(s), base) + } +} + + +func BenchmarkStringShort2(b *testing.B) { + StringHelper(b, 2, shortBase, shortExponent) +} + + +func BenchmarkStringShort8(b *testing.B) { + StringHelper(b, 8, shortBase, shortExponent) +} + + +func BenchmarkStringShort10(b *testing.B) { + StringHelper(b, 10, shortBase, shortExponent) +} + + +func BenchmarkStringShort16(b *testing.B) { + StringHelper(b, 16, shortBase, shortExponent) +} + + +func BenchmarkStringMedium2(b *testing.B) { + StringHelper(b, 2, mediumBase, mediumExponent) +} + + +func BenchmarkStringMedium8(b *testing.B) { + StringHelper(b, 8, mediumBase, mediumExponent) +} + + +func BenchmarkStringMedium10(b *testing.B) { + StringHelper(b, 10, mediumBase, mediumExponent) +} + + +func BenchmarkStringMedium16(b *testing.B) { + StringHelper(b, 16, mediumBase, mediumExponent) +} + + +func BenchmarkStringLong2(b *testing.B) { + StringHelper(b, 2, longBase, longExponent) +} + + +func BenchmarkStringLong8(b *testing.B) { + StringHelper(b, 8, longBase, longExponent) +} + + +func BenchmarkStringLong10(b *testing.B) { + StringHelper(b, 10, longBase, longExponent) +} + + +func BenchmarkStringLong16(b *testing.B) { + StringHelper(b, 16, longBase, longExponent) +} + + +func StringHelper(b *testing.B, base int, xv, yv Word) { + b.StopTimer() + var x, y, z nat + x = x.setWord(xv) + y = y.setWord(yv) + z = z.expNN(x, y, nil) + b.StartTimer() + + for i := 0; i < b.N; i++ { + z.string(lowercaseDigits[0:base]) + } +} + + func TestLeadingZeros(t *testing.T) { var x Word = _B >> 1 for i := 0; i <= _W; i++ { @@ -479,7 +691,6 @@ func TestTrailingZeroBits(t *testing.T) { } } - var expNNTests = []struct { x, y, m string out string diff --git a/src/pkg/big/rat.go b/src/pkg/big/rat.go index f11c27425..1fbf8c459 100644 --- a/src/pkg/big/rat.go +++ b/src/pkg/big/rat.go @@ -7,6 +7,7 @@ package big import ( + "encoding/binary" "fmt" "os" "strings" @@ -314,7 +315,11 @@ func (z *Rat) RatString() string { // digits of precision after the decimal point and the last digit rounded. func (z *Rat) FloatString(prec int) string { if z.IsInt() { - return z.a.String() + s := z.a.String() + if prec > 0 { + s += "." + strings.Repeat("0", prec) + } + return s } q, r := nat{}.div(nat{}, z.a.abs, z.b) @@ -350,3 +355,45 @@ func (z *Rat) FloatString(prec int) string { return s } + + +// Gob codec version. Permits backward-compatible changes to the encoding. +const ratGobVersion byte = 1 + +// GobEncode implements the gob.GobEncoder interface. +func (z *Rat) GobEncode() ([]byte, os.Error) { + buf := make([]byte, 1+4+(len(z.a.abs)+len(z.b))*_S) // extra bytes for version and sign bit (1), and numerator length (4) + i := z.b.bytes(buf) + j := z.a.abs.bytes(buf[0:i]) + n := i - j + if int(uint32(n)) != n { + // this should never happen + return nil, os.ErrorString("Rat.GobEncode: numerator too large") + } + binary.BigEndian.PutUint32(buf[j-4:j], uint32(n)) + j -= 1 + 4 + b := ratGobVersion << 1 // make space for sign bit + if z.a.neg { + b |= 1 + } + buf[j] = b + return buf[j:], nil +} + + +// GobDecode implements the gob.GobDecoder interface. +func (z *Rat) GobDecode(buf []byte) os.Error { + if len(buf) == 0 { + return os.ErrorString("Rat.GobDecode: no data") + } + b := buf[0] + if b>>1 != ratGobVersion { + return os.ErrorString(fmt.Sprintf("Rat.GobDecode: encoding version %d not supported", b>>1)) + } + const j = 1 + 4 + i := j + binary.BigEndian.Uint32(buf[j-4:j]) + z.a.neg = b&1 != 0 + z.a.abs = z.a.abs.setBytes(buf[j:i]) + z.b = z.b.setBytes(buf[i:]) + return nil +} diff --git a/src/pkg/big/rat_test.go b/src/pkg/big/rat_test.go index ae5c7c993..e64505ea3 100644 --- a/src/pkg/big/rat_test.go +++ b/src/pkg/big/rat_test.go @@ -7,6 +7,7 @@ package big import ( "bytes" "fmt" + "gob" "testing" ) @@ -86,12 +87,13 @@ var floatStringTests = []struct { out string }{ {"0", 0, "0"}, - {"0", 4, "0"}, + {"0", 4, "0.0000"}, {"1", 0, "1"}, - {"1", 2, "1"}, + {"1", 2, "1.00"}, {"-1", 0, "-1"}, {".25", 2, "0.25"}, {".25", 1, "0.3"}, + {".25", 3, "0.250"}, {"-1/3", 3, "-0.333"}, {"-2/3", 4, "-0.6667"}, {"0.96", 1, "1.0"}, @@ -307,3 +309,36 @@ func TestRatSetFrac64Rat(t *testing.T) { } } } + + +func TestRatGobEncoding(t *testing.T) { + var medium bytes.Buffer + enc := gob.NewEncoder(&medium) + dec := gob.NewDecoder(&medium) + for i, test := range gobEncodingTests { + for j := 0; j < 4; j++ { + medium.Reset() // empty buffer for each test case (in case of failures) + stest := test + if j&1 != 0 { + // negative numbers + stest = "-" + test + } + if j%2 != 0 { + // fractions + stest = stest + "." + test + } + var tx Rat + tx.SetString(stest) + if err := enc.Encode(&tx); err != nil { + t.Errorf("#%d%c: encoding failed: %s", i, 'a'+j, err) + } + var rx Rat + if err := dec.Decode(&rx); err != nil { + t.Errorf("#%d%c: decoding failed: %s", i, 'a'+j, err) + } + if rx.Cmp(&tx) != 0 { + t.Errorf("#%d%c: transmission failed: got %s want %s", i, 'a'+j, &rx, &tx) + } + } + } +} diff --git a/src/pkg/compress/lzw/reader.go b/src/pkg/compress/lzw/reader.go index a1cd2abc0..21231c8e5 100644 --- a/src/pkg/compress/lzw/reader.go +++ b/src/pkg/compress/lzw/reader.go @@ -32,13 +32,49 @@ const ( MSB ) +const ( + maxWidth = 12 + decoderInvalidCode = 0xffff + flushBuffer = 1 << maxWidth +) + // decoder is the state from which the readXxx method converts a byte // stream into a code stream. type decoder struct { - r io.ByteReader - bits uint32 - nBits uint - width uint + r io.ByteReader + bits uint32 + nBits uint + width uint + read func(*decoder) (uint16, os.Error) // readLSB or readMSB + litWidth int // width in bits of literal codes + err os.Error + + // The first 1<<litWidth codes are literal codes. + // The next two codes mean clear and EOF. + // Other valid codes are in the range [lo, hi] where lo := clear + 2, + // with the upper bound incrementing on each code seen. + // overflow is the code at which hi overflows the code width. + // last is the most recently seen code, or decoderInvalidCode. + clear, eof, hi, overflow, last uint16 + + // Each code c in [lo, hi] expands to two or more bytes. For c != hi: + // suffix[c] is the last of these bytes. + // prefix[c] is the code for all but the last byte. + // This code can either be a literal code or another code in [lo, c). + // The c == hi case is a special case. + suffix [1 << maxWidth]uint8 + prefix [1 << maxWidth]uint16 + + // output is the temporary output buffer. + // Literal codes are accumulated from the start of the buffer. + // Non-literal codes decode to a sequence of suffixes that are first + // written right-to-left from the end of the buffer before being copied + // to the start of the buffer. + // It is flushed when it contains >= 1<<maxWidth bytes, + // so that there is always room to decode an entire code. + output [2 * 1 << maxWidth]byte + o int // write index into output + toRead []byte // bytes to return from Read } // readLSB returns the next code for "Least Significant Bits first" data. @@ -73,119 +109,113 @@ func (d *decoder) readMSB() (uint16, os.Error) { return code, nil } -// decode decompresses bytes from r and writes them to pw. -// read specifies how to decode bytes into codes. -// litWidth is the width in bits of literal codes. -func decode(r io.Reader, read func(*decoder) (uint16, os.Error), litWidth int, pw *io.PipeWriter) { - br, ok := r.(io.ByteReader) - if !ok { - br = bufio.NewReader(r) +func (d *decoder) Read(b []byte) (int, os.Error) { + for { + if len(d.toRead) > 0 { + n := copy(b, d.toRead) + d.toRead = d.toRead[n:] + return n, nil + } + if d.err != nil { + return 0, d.err + } + d.decode() } - pw.CloseWithError(decode1(pw, br, read, uint(litWidth))) + panic("unreachable") } -func decode1(pw *io.PipeWriter, r io.ByteReader, read func(*decoder) (uint16, os.Error), litWidth uint) os.Error { - const ( - maxWidth = 12 - invalidCode = 0xffff - ) - d := decoder{r, 0, 0, 1 + litWidth} - w := bufio.NewWriter(pw) - // The first 1<<litWidth codes are literal codes. - // The next two codes mean clear and EOF. - // Other valid codes are in the range [lo, hi] where lo := clear + 2, - // with the upper bound incrementing on each code seen. - clear := uint16(1) << litWidth - eof, hi := clear+1, clear+1 - // overflow is the code at which hi overflows the code width. - overflow := uint16(1) << d.width - var ( - // Each code c in [lo, hi] expands to two or more bytes. For c != hi: - // suffix[c] is the last of these bytes. - // prefix[c] is the code for all but the last byte. - // This code can either be a literal code or another code in [lo, c). - // The c == hi case is a special case. - suffix [1 << maxWidth]uint8 - prefix [1 << maxWidth]uint16 - // buf is a scratch buffer for reconstituting the bytes that a code expands to. - // Code suffixes are written right-to-left from the end of the buffer. - buf [1 << maxWidth]byte - ) - +// decode decompresses bytes from r and leaves them in d.toRead. +// read specifies how to decode bytes into codes. +// litWidth is the width in bits of literal codes. +func (d *decoder) decode() { // Loop over the code stream, converting codes into decompressed bytes. - last := uint16(invalidCode) for { - code, err := read(&d) + code, err := d.read(d) if err != nil { if err == os.EOF { err = io.ErrUnexpectedEOF } - return err + d.err = err + return } switch { - case code < clear: + case code < d.clear: // We have a literal code. - if err := w.WriteByte(uint8(code)); err != nil { - return err - } - if last != invalidCode { + d.output[d.o] = uint8(code) + d.o++ + if d.last != decoderInvalidCode { // Save what the hi code expands to. - suffix[hi] = uint8(code) - prefix[hi] = last + d.suffix[d.hi] = uint8(code) + d.prefix[d.hi] = d.last } - case code == clear: - d.width = 1 + litWidth - hi = eof - overflow = 1 << d.width - last = invalidCode + case code == d.clear: + d.width = 1 + uint(d.litWidth) + d.hi = d.eof + d.overflow = 1 << d.width + d.last = decoderInvalidCode continue - case code == eof: - return w.Flush() - case code <= hi: - c, i := code, len(buf)-1 - if code == hi { + case code == d.eof: + d.flush() + d.err = os.EOF + return + case code <= d.hi: + c, i := code, len(d.output)-1 + if code == d.hi { // code == hi is a special case which expands to the last expansion // followed by the head of the last expansion. To find the head, we walk // the prefix chain until we find a literal code. - c = last - for c >= clear { - c = prefix[c] + c = d.last + for c >= d.clear { + c = d.prefix[c] } - buf[i] = uint8(c) + d.output[i] = uint8(c) i-- - c = last + c = d.last } - // Copy the suffix chain into buf and then write that to w. - for c >= clear { - buf[i] = suffix[c] + // Copy the suffix chain into output and then write that to w. + for c >= d.clear { + d.output[i] = d.suffix[c] i-- - c = prefix[c] + c = d.prefix[c] } - buf[i] = uint8(c) - if _, err := w.Write(buf[i:]); err != nil { - return err - } - if last != invalidCode { + d.output[i] = uint8(c) + d.o += copy(d.output[d.o:], d.output[i:]) + if d.last != decoderInvalidCode { // Save what the hi code expands to. - suffix[hi] = uint8(c) - prefix[hi] = last + d.suffix[d.hi] = uint8(c) + d.prefix[d.hi] = d.last } default: - return os.NewError("lzw: invalid code") + d.err = os.NewError("lzw: invalid code") + return } - last, hi = code, hi+1 - if hi >= overflow { + d.last, d.hi = code, d.hi+1 + if d.hi >= d.overflow { if d.width == maxWidth { - last = invalidCode - continue + d.last = decoderInvalidCode + } else { + d.width++ + d.overflow <<= 1 } - d.width++ - overflow <<= 1 + } + if d.o >= flushBuffer { + d.flush() + return } } panic("unreachable") } +func (d *decoder) flush() { + d.toRead = d.output[:d.o] + d.o = 0 +} + +func (d *decoder) Close() os.Error { + d.err = os.EINVAL // in case any Reads come along + return nil +} + // NewReader creates a new io.ReadCloser that satisfies reads by decompressing // the data read from r. // It is the caller's responsibility to call Close on the ReadCloser when @@ -193,21 +223,31 @@ func decode1(pw *io.PipeWriter, r io.ByteReader, read func(*decoder) (uint16, os // The number of bits to use for literal codes, litWidth, must be in the // range [2,8] and is typically 8. func NewReader(r io.Reader, order Order, litWidth int) io.ReadCloser { - pr, pw := io.Pipe() - var read func(*decoder) (uint16, os.Error) + d := new(decoder) switch order { case LSB: - read = (*decoder).readLSB + d.read = (*decoder).readLSB case MSB: - read = (*decoder).readMSB + d.read = (*decoder).readMSB default: - pw.CloseWithError(os.NewError("lzw: unknown order")) - return pr + d.err = os.NewError("lzw: unknown order") + return d } if litWidth < 2 || 8 < litWidth { - pw.CloseWithError(fmt.Errorf("lzw: litWidth %d out of range", litWidth)) - return pr + d.err = fmt.Errorf("lzw: litWidth %d out of range", litWidth) + return d } - go decode(r, read, litWidth, pw) - return pr + if br, ok := r.(io.ByteReader); ok { + d.r = br + } else { + d.r = bufio.NewReader(r) + } + d.litWidth = litWidth + d.width = 1 + uint(litWidth) + d.clear = uint16(1) << uint(litWidth) + d.eof, d.hi = d.clear+1, d.clear+1 + d.overflow = uint16(1) << d.width + d.last = decoderInvalidCode + + return d } diff --git a/src/pkg/compress/lzw/writer_test.go b/src/pkg/compress/lzw/writer_test.go index 82464ecd1..4c5e522f9 100644 --- a/src/pkg/compress/lzw/writer_test.go +++ b/src/pkg/compress/lzw/writer_test.go @@ -77,13 +77,13 @@ func testFile(t *testing.T, fn string, order Order, litWidth int) { t.Errorf("%s (order=%d litWidth=%d): %v", fn, order, litWidth, err1) return } - if len(b0) != len(b1) { - t.Errorf("%s (order=%d litWidth=%d): length mismatch %d versus %d", fn, order, litWidth, len(b0), len(b1)) + if len(b1) != len(b0) { + t.Errorf("%s (order=%d litWidth=%d): length mismatch %d != %d", fn, order, litWidth, len(b1), len(b0)) return } for i := 0; i < len(b0); i++ { - if b0[i] != b1[i] { - t.Errorf("%s (order=%d litWidth=%d): mismatch at %d, 0x%02x versus 0x%02x\n", fn, order, litWidth, i, b0[i], b1[i]) + if b1[i] != b0[i] { + t.Errorf("%s (order=%d litWidth=%d): mismatch at %d, 0x%02x != 0x%02x\n", fn, order, litWidth, i, b1[i], b0[i]) return } } diff --git a/src/pkg/crypto/ocsp/ocsp.go b/src/pkg/crypto/ocsp/ocsp.go index acd75b8b0..57dbe7d2d 100644 --- a/src/pkg/crypto/ocsp/ocsp.go +++ b/src/pkg/crypto/ocsp/ocsp.go @@ -13,6 +13,7 @@ import ( "crypto/rsa" _ "crypto/sha1" "crypto/x509" + "crypto/x509/pkix" "os" "time" ) @@ -32,21 +33,9 @@ const ( ocspUnauthorized = 5 ) -type rdnSequence []relativeDistinguishedNameSET - -type relativeDistinguishedNameSET []attributeTypeAndValue - -type attributeTypeAndValue struct { - Type asn1.ObjectIdentifier - Value interface{} -} - -type algorithmIdentifier struct { - Algorithm asn1.ObjectIdentifier -} type certID struct { - HashAlgorithm algorithmIdentifier + HashAlgorithm pkix.AlgorithmIdentifier NameHash []byte IssuerKeyHash []byte SerialNumber asn1.RawValue @@ -64,16 +53,16 @@ type responseBytes struct { type basicResponse struct { TBSResponseData responseData - SignatureAlgorithm algorithmIdentifier + SignatureAlgorithm pkix.AlgorithmIdentifier Signature asn1.BitString Certificates []asn1.RawValue "explicit,tag:0,optional" } type responseData struct { Raw asn1.RawContent - Version int "optional,default:1,explicit,tag:0" - RequestorName rdnSequence "optional,explicit,tag:1" - KeyHash []byte "optional,explicit,tag:2" + Version int "optional,default:1,explicit,tag:0" + RequestorName pkix.RDNSequence "optional,explicit,tag:1" + KeyHash []byte "optional,explicit,tag:2" ProducedAt *time.Time Responses []singleResponse } diff --git a/src/pkg/crypto/rsa/rsa.go b/src/pkg/crypto/rsa/rsa.go index 6bfe6c4e5..380f71570 100644 --- a/src/pkg/crypto/rsa/rsa.go +++ b/src/pkg/crypto/rsa/rsa.go @@ -64,7 +64,7 @@ func (priv *PrivateKey) Validate() os.Error { // easy for an attack to generate composites that pass this test. for _, prime := range priv.Primes { if !big.ProbablyPrime(prime, 20) { - return os.ErrorString("Prime factor is composite") + return os.ErrorString("prime factor is composite") } } diff --git a/src/pkg/crypto/tls/tls.go b/src/pkg/crypto/tls/tls.go index 7d0bb9f34..9e5c9270a 100644 --- a/src/pkg/crypto/tls/tls.go +++ b/src/pkg/crypto/tls/tls.go @@ -159,7 +159,7 @@ func X509KeyPair(certPEMBlock, keyPEMBlock []byte) (cert Certificate, err os.Err key, err := x509.ParsePKCS1PrivateKey(keyDERBlock.Bytes) if err != nil { - err = os.ErrorString("crypto/tls: failed to parse key") + err = os.ErrorString("crypto/tls: failed to parse key: " + err.String()) return } diff --git a/src/pkg/crypto/twofish/twofish.go b/src/pkg/crypto/twofish/twofish.go index 9303f03ff..1a1aac9b9 100644 --- a/src/pkg/crypto/twofish/twofish.go +++ b/src/pkg/crypto/twofish/twofish.go @@ -116,7 +116,7 @@ func (c *Cipher) Reset() { c.k[i] = 0 } for i := range c.s { - for j := 0; j < 265; j++ { + for j := 0; j < 256; j++ { c.s[i][j] = 0 } } diff --git a/src/pkg/crypto/x509/cert_pool.go b/src/pkg/crypto/x509/cert_pool.go index c295fd97e..16cd92efc 100644 --- a/src/pkg/crypto/x509/cert_pool.go +++ b/src/pkg/crypto/x509/cert_pool.go @@ -5,6 +5,7 @@ package x509 import ( + "crypto/x509/pkix" "encoding/pem" "strings" ) @@ -25,7 +26,7 @@ func NewCertPool() *CertPool { } } -func nameToKey(name *Name) string { +func nameToKey(name *pkix.Name) string { return strings.Join(name.Country, ",") + "/" + strings.Join(name.Organization, ",") + "/" + strings.Join(name.OrganizationalUnit, ",") + "/" + name.CommonName } diff --git a/src/pkg/crypto/x509/crl/crl.go b/src/pkg/crypto/x509/crl/crl.go deleted file mode 100644 index c79c797c7..000000000 --- a/src/pkg/crypto/x509/crl/crl.go +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package crl exposes low-level details of PKIX Certificate Revocation Lists -// as specified in RFC 5280, section 5. -package crl - -import ( - "asn1" - "bytes" - "encoding/pem" - "os" - "time" -) - -// CertificateList represents the ASN.1 structure of the same name. See RFC -// 5280, section 5.1. Use crypto/x509/Certificate.CheckCRLSignature to verify -// the signature. -type CertificateList struct { - TBSCertList TBSCertificateList - SignatureAlgorithm AlgorithmIdentifier - SignatureValue asn1.BitString -} - -// HasExpired returns true iff currentTimeSeconds is past the expiry time of -// certList. -func (certList *CertificateList) HasExpired(currentTimeSeconds int64) bool { - return certList.TBSCertList.NextUpdate.Seconds() <= currentTimeSeconds -} - -// TBSCertificateList represents the ASN.1 structure of the same name. See RFC -// 5280, section 5.1. -type TBSCertificateList struct { - Raw asn1.RawContent - Version int "optional,default:2" - Signature AlgorithmIdentifier - Issuer asn1.RawValue - ThisUpdate *time.Time - NextUpdate *time.Time - RevokedCertificates []RevokedCertificate "optional" - Extensions []Extension "tag:0,optional,explicit" -} - -// AlgorithmIdentifier represents the ASN.1 structure of the same name. See RFC -// 5280, section 4.1.1.2. -type AlgorithmIdentifier struct { - Algo asn1.ObjectIdentifier - Params asn1.RawValue "optional" -} - -// AlgorithmIdentifier represents the ASN.1 structure of the same name. See RFC -// 5280, section 5.1. -type RevokedCertificate struct { - SerialNumber asn1.RawValue - RevocationTime *time.Time - Extensions []Extension "optional" -} - -// AlgorithmIdentifier represents the ASN.1 structure of the same name. See RFC -// 5280, section 4.2. -type Extension struct { - Id asn1.ObjectIdentifier - IsCritial bool "optional" - Value []byte -} - -// pemCRLPrefix is the magic string that indicates that we have a PEM encoded -// CRL. -var pemCRLPrefix = []byte("-----BEGIN X509 CRL") -// pemType is the type of a PEM encoded CRL. -var pemType = "X509 CRL" - -// Parse parses a CRL from the given bytes. It's often the case that PEM -// encoded CRLs will appear where they should be DER encoded, so this function -// will transparently handle PEM encoding as long as there isn't any leading -// garbage. -func Parse(crlBytes []byte) (certList *CertificateList, err os.Error) { - if bytes.HasPrefix(crlBytes, pemCRLPrefix) { - block, _ := pem.Decode(crlBytes) - if block != nil && block.Type == pemType { - crlBytes = block.Bytes - } - } - return ParseDER(crlBytes) -} - -// ParseDER parses a DER encoded CRL from the given bytes. -func ParseDER(derBytes []byte) (certList *CertificateList, err os.Error) { - certList = new(CertificateList) - _, err = asn1.Unmarshal(derBytes, certList) - if err != nil { - certList = nil - } - return -} diff --git a/src/pkg/crypto/x509/crl/crl_test.go b/src/pkg/crypto/x509/crl/crl_test.go deleted file mode 100644 index 62d8dc195..000000000 --- a/src/pkg/crypto/x509/crl/crl_test.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package crl - -import ( - "encoding/base64" - "testing" -) - -func fromBase64(in string) []byte { - out := make([]byte, base64.StdEncoding.DecodedLen(len(in))) - _, err := base64.StdEncoding.Decode(out, []byte(in)) - if err != nil { - panic("failed to base64 decode") - } - return out -} - -func TestParseDER(t *testing.T) { - derBytes := fromBase64(derCRLBase64) - certList, err := ParseDER(derBytes) - if err != nil { - t.Errorf("error parsing: %s", err) - return - } - numCerts := len(certList.TBSCertList.RevokedCertificates) - expected := 88 - if numCerts != expected { - t.Errorf("bad number of revoked certificates. got: %d want: %d", numCerts, expected) - } - - if certList.HasExpired(1302517272) { - t.Errorf("CRL has expired (but shouldn't have)") - } - - // Can't check the signature here without a package cycle. -} - -func TestParsePEM(t *testing.T) { - pemBytes := fromBase64(pemCRLBase64) - certList, err := Parse(pemBytes) - if err != nil { - t.Errorf("error parsing: %s", err) - return - } - numCerts := len(certList.TBSCertList.RevokedCertificates) - expected := 2 - if numCerts != expected { - t.Errorf("bad number of revoked certificates. got: %d want: %d", numCerts, expected) - } - - if certList.HasExpired(1302517272) { - t.Errorf("CRL has expired (but shouldn't have)") - } - - // Can't check the signature here without a package cycle. -} - -const derCRLBase64 = "MIINqzCCDJMCAQEwDQYJKoZIhvcNAQEFBQAwVjEZMBcGA1UEAxMQUEtJIEZJTk1FQ0NBTklDQTEVMBMGA1UEChMMRklOTUVDQ0FOSUNBMRUwEwYDVQQLEwxGSU5NRUNDQU5JQ0ExCzAJBgNVBAYTAklUFw0xMTA1MDQxNjU3NDJaFw0xMTA1MDQyMDU3NDJaMIIMBzAhAg4Ze1od49Lt1qIXBydAzhcNMDkwNzE2MDg0MzIyWjAAMCECDl0HSL9bcZ1Ci/UHJ0DPFw0wOTA3MTYwODQzMTNaMAAwIQIOESB9tVAmX3cY7QcnQNAXDTA5MDcxNjA4NDUyMlowADAhAg4S1tGAQ3mHt8uVBydA1RcNMDkwODA0MTUyNTIyWjAAMCECDlQ249Y7vtC25ScHJ0DWFw0wOTA4MDQxNTI1MzdaMAAwIQIOISMop3NkA4PfYwcnQNkXDTA5MDgwNDExMDAzNFowADAhAg56/BMoS29KEShTBydA2hcNMDkwODA0MTEwMTAzWjAAMCECDnBp/22HPH5CSWoHJ0DbFw0wOTA4MDQxMDU0NDlaMAAwIQIOV9IP+8CD8bK+XAcnQNwXDTA5MDgwNDEwNTcxN1owADAhAg4v5aRz0IxWqYiXBydA3RcNMDkwODA0MTA1NzQ1WjAAMCECDlOU34VzvZAybQwHJ0DeFw0wOTA4MDQxMDU4MjFaMAAwIAINO4CD9lluIxcwBydBAxcNMDkwNzIyMTUzMTU5WjAAMCECDgOllfO8Y1QA7/wHJ0ExFw0wOTA3MjQxMTQxNDNaMAAwIQIOJBX7jbiCdRdyjgcnQUQXDTA5MDkxNjA5MzAwOFowADAhAg5iYSAgmDrlH/RZBydBRRcNMDkwOTE2MDkzMDE3WjAAMCECDmu6k6srP3jcMaQHJ0FRFw0wOTA4MDQxMDU2NDBaMAAwIQIOX8aHlO0V+WVH4QcnQVMXDTA5MDgwNDEwNTcyOVowADAhAg5flK2rg3NnsRgDBydBzhcNMTEwMjAxMTUzMzQ2WjAAMCECDg35yJDL1jOPTgoHJ0HPFw0xMTAyMDExNTM0MjZaMAAwIQIOMyFJ6+e9iiGVBQcnQdAXDTA5MDkxODEzMjAwNVowADAhAg5Emb/Oykucmn8fBydB1xcNMDkwOTIxMTAxMDQ3WjAAMCECDjQKCncV+MnUavMHJ0HaFw0wOTA5MjIwODE1MjZaMAAwIQIOaxiFUt3dpd+tPwcnQfQXDTEwMDYxODA4NDI1MVowADAhAg5G7P8nO0tkrMt7BydB9RcNMTAwNjE4MDg0MjMwWjAAMCECDmTCC3SXhmDRst4HJ0H2Fw0wOTA5MjgxMjA3MjBaMAAwIQIOHoGhUr/pRwzTKgcnQfcXDTA5MDkyODEyMDcyNFowADAhAg50wrcrCiw8mQmPBydCBBcNMTAwMjE2MTMwMTA2WjAAMCECDifWmkvwyhEqwEcHJ0IFFw0xMDAyMTYxMzAxMjBaMAAwIQIOfgPmlW9fg+osNgcnQhwXDTEwMDQxMzA5NTIwMFowADAhAg4YHAGuA6LgCk7tBydCHRcNMTAwNDEzMDk1MTM4WjAAMCECDi1zH1bxkNJhokAHJ0IsFw0xMDA0MTMwOTU5MzBaMAAwIQIOMipNccsb/wo2fwcnQi0XDTEwMDQxMzA5NTkwMFowADAhAg46lCmvPl4GpP6ABydCShcNMTAwMTE5MDk1MjE3WjAAMCECDjaTcaj+wBpcGAsHJ0JLFw0xMDAxMTkwOTUyMzRaMAAwIQIOOMC13EOrBuxIOQcnQloXDTEwMDIwMTA5NDcwNVowADAhAg5KmZl+krz4RsmrBydCWxcNMTAwMjAxMDk0NjQwWjAAMCECDmLG3zQJ/fzdSsUHJ0JiFw0xMDAzMDEwOTUxNDBaMAAwIQIOP39ksgHdojf4owcnQmMXDTEwMDMwMTA5NTExN1owADAhAg4LDQzvWNRlD6v9BydCZBcNMTAwMzAxMDk0NjIyWjAAMCECDkmNfeclaFhIaaUHJ0JlFw0xMDAzMDEwOTQ2MDVaMAAwIQIOT/qWWfpH/m8NTwcnQpQXDTEwMDUxMTA5MTgyMVowADAhAg5m/ksYxvCEgJSvBydClRcNMTAwNTExMDkxODAxWjAAMCECDgvf3Ohq6JOPU9AHJ0KWFw0xMDA1MTEwOTIxMjNaMAAwIQIOKSPas10z4jNVIQcnQpcXDTEwMDUxMTA5MjEwMlowADAhAg4mCWmhoZ3lyKCDBydCohcNMTEwNDI4MTEwMjI1WjAAMCECDkeiyRsBMK0Gvr4HJ0KjFw0xMTA0MjgxMTAyMDdaMAAwIQIOa09b/nH2+55SSwcnQq4XDTExMDQwMTA4Mjk0NlowADAhAg5O7M7iq7gGplr1BydCrxcNMTEwNDAxMDgzMDE3WjAAMCECDjlT6mJxUjTvyogHJ0K1Fw0xMTAxMjcxNTQ4NTJaMAAwIQIODS/l4UUFLe21NAcnQrYXDTExMDEyNzE1NDgyOFowADAhAg5lPRA0XdOUF6lSBydDHhcNMTEwMTI4MTQzNTA1WjAAMCECDixKX4fFGGpENwgHJ0MfFw0xMTAxMjgxNDM1MzBaMAAwIQIORNBkqsPnpKTtbAcnQ08XDTEwMDkwOTA4NDg0MlowADAhAg5QL+EMM3lohedEBydDUBcNMTAwOTA5MDg0ODE5WjAAMCECDlhDnHK+HiTRAXcHJ0NUFw0xMDEwMTkxNjIxNDBaMAAwIQIOdBFqAzq/INz53gcnQ1UXDTEwMTAxOTE2MjA0NFowADAhAg4OjR7s8MgKles1BydDWhcNMTEwMTI3MTY1MzM2WjAAMCECDmfR/elHee+d0SoHJ0NbFw0xMTAxMjcxNjUzNTZaMAAwIQIOBTKv2ui+KFMI+wcnQ5YXDTEwMDkxNTEwMjE1N1owADAhAg49F3c/GSah+oRUBydDmxcNMTEwMTI3MTczMjMzWjAAMCECDggv4I61WwpKFMMHJ0OcFw0xMTAxMjcxNzMyNTVaMAAwIQIOXx/Y8sEvwS10LAcnQ6UXDTExMDEyODExMjkzN1owADAhAg5LSLbnVrSKaw/9BydDphcNMTEwMTI4MTEyOTIwWjAAMCECDmFFoCuhKUeACQQHJ0PfFw0xMTAxMTExMDE3MzdaMAAwIQIOQTDdFh2fSPF6AAcnQ+AXDTExMDExMTEwMTcxMFowADAhAg5B8AOXX61FpvbbBydD5RcNMTAxMDA2MTAxNDM2WjAAMCECDh41P2Gmi7PkwI4HJ0PmFw0xMDEwMDYxMDE2MjVaMAAwIQIOWUHGLQCd+Ale9gcnQ/0XDTExMDUwMjA3NTYxMFowADAhAg5Z2c9AYkikmgWOBydD/hcNMTEwNTAyMDc1NjM0WjAAMCECDmf/UD+/h8nf+74HJ0QVFw0xMTA0MTUwNzI4MzNaMAAwIQIOICvj4epy3MrqfwcnRBYXDTExMDQxNTA3Mjg1NlowADAhAg4bouRMfOYqgv4xBydEHxcNMTEwMzA4MTYyNDI1WjAAMCECDhebWHGoKiTp7pEHJ0QgFw0xMTAzMDgxNjI0NDhaMAAwIQIOX+qnxxAqJ8LtawcnRDcXDTExMDEzMTE1MTIyOFowADAhAg4j0fICqZ+wkOdqBydEOBcNMTEwMTMxMTUxMTQxWjAAMCECDhmXjsV4SUpWtAMHJ0RLFw0xMTAxMjgxMTI0MTJaMAAwIQIODno/w+zG43kkTwcnREwXDTExMDEyODExMjM1MlowADAhAg4b1gc88767Fr+LBydETxcNMTEwMTI4MTEwMjA4WjAAMCECDn+M3Pa1w2nyFeUHJ0RQFw0xMTAxMjgxMDU4NDVaMAAwIQIOaduoyIH61tqybAcnRJUXDTEwMTIxNTA5NDMyMlowADAhAg4nLqQPkyi3ESAKBydElhcNMTAxMjE1MDk0MzM2WjAAMCECDi504NIMH8578gQHJ0SbFw0xMTAyMTQxNDA1NDFaMAAwIQIOGuaM8PDaC5u1egcnRJwXDTExMDIxNDE0MDYwNFowADAhAg4ehYq/BXGnB5PWBydEnxcNMTEwMjA0MDgwOTUxWjAAMCECDkSD4eS4FxW5H20HJ0SgFw0xMTAyMDQwODA5MjVaMAAwIQIOOCcb6ilYObt1egcnRKEXDTExMDEyNjEwNDEyOVowADAhAg58tISWCCwFnKGnBydEohcNMTEwMjA0MDgxMzQyWjAAMCECDn5rjtabY/L/WL0HJ0TJFw0xMTAyMDQxMTAzNDFaMAAwDQYJKoZIhvcNAQEFBQADggEBAGnF2Gs0+LNiYCW1Ipm83OXQYP/bd5tFFRzyz3iepFqNfYs4D68/QihjFoRHQoXEB0OEe1tvaVnnPGnEOpi6krwekquMxo4H88B5SlyiFIqemCOIss0SxlCFs69LmfRYvPPvPEhoXtQ3ZThe0UvKG83GOklhvGl6OaiRf4Mt+m8zOT4Wox/j6aOBK6cw6qKCdmD+Yj1rrNqFGg1CnSWMoD6S6mwNgkzwdBUJZ22BwrzAAo4RHa2Uy3ef1FjwD0XtU5N3uDSxGGBEDvOe5z82rps3E22FpAA8eYl8kaXtmWqyvYU0epp4brGuTxCuBMCAsxt/OjIjeNNQbBGkwxgfYA0=" - -const pemCRLBase64 = "LS0tLS1CRUdJTiBYNTA5IENSTC0tLS0tDQpNSUlCOWpDQ0FWOENBUUV3RFFZSktvWklodmNOQVFFRkJRQXdiREVhTUJnR0ExVUVDaE1SVWxOQklGTmxZM1Z5DQphWFI1SUVsdVl5NHhIakFjQmdOVkJBTVRGVkpUUVNCUWRXSnNhV01nVW05dmRDQkRRU0IyTVRFdU1Dd0dDU3FHDQpTSWIzRFFFSkFSWWZjbk5oYTJWdmJuSnZiM1J6YVdkdVFISnpZWE5sWTNWeWFYUjVMbU52YlJjTk1URXdNakl6DQpNVGt5T0RNd1doY05NVEV3T0RJeU1Ua3lPRE13V2pDQmpEQktBaEVBckRxb2g5RkhKSFhUN09QZ3V1bjQrQmNODQpNRGt4TVRBeU1UUXlOekE1V2pBbU1Bb0dBMVVkRlFRRENnRUpNQmdHQTFVZEdBUVJHQTh5TURBNU1URXdNakUwDQpNalExTlZvd1BnSVJBTEd6blowOTVQQjVhQU9MUGc1N2ZNTVhEVEF5TVRBeU16RTBOVEF4TkZvd0dqQVlCZ05WDQpIUmdFRVJnUE1qQXdNakV3TWpNeE5EVXdNVFJhb0RBd0xqQWZCZ05WSFNNRUdEQVdnQlQxVERGNlVRTS9MTmVMDQpsNWx2cUhHUXEzZzltekFMQmdOVkhSUUVCQUlDQUlRd0RRWUpLb1pJaHZjTkFRRUZCUUFEZ1lFQUZVNUFzNk16DQpxNVBSc2lmYW9iUVBHaDFhSkx5QytNczVBZ2MwYld5QTNHQWR4dXI1U3BQWmVSV0NCamlQL01FSEJXSkNsQkhQDQpHUmNxNXlJZDNFakRrYUV5eFJhK2k2N0x6dmhJNmMyOUVlNks5cFNZd2ppLzdSVWhtbW5Qclh0VHhsTDBsckxyDQptUVFKNnhoRFJhNUczUUE0Q21VZHNITnZicnpnbUNZcHZWRT0NCi0tLS0tRU5EIFg1MDkgQ1JMLS0tLS0NCg0K" diff --git a/src/pkg/crypto/x509/crl/Makefile b/src/pkg/crypto/x509/pkix/Makefile index d780af38e..e29b74c01 100644 --- a/src/pkg/crypto/x509/crl/Makefile +++ b/src/pkg/crypto/x509/pkix/Makefile @@ -4,8 +4,8 @@ include ../../../../Make.inc -TARG=crypto/x509/crl +TARG=crypto/x509/pkix GOFILES=\ - crl.go\ + pkix.go\ include ../../../../Make.pkg diff --git a/src/pkg/crypto/x509/pkix/pkix.go b/src/pkg/crypto/x509/pkix/pkix.go new file mode 100644 index 000000000..7806b2a2e --- /dev/null +++ b/src/pkg/crypto/x509/pkix/pkix.go @@ -0,0 +1,167 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package pkix contains shared, low level structures used for ASN.1 parsing +// and serialization of X.509 certificates, CRL and OCSP. +package pkix + +import ( + "asn1" + "big" + "time" +) + +// AlgorithmIdentifier represents the ASN.1 structure of the same name. See RFC +// 5280, section 4.1.1.2. +type AlgorithmIdentifier struct { + Algorithm asn1.ObjectIdentifier + Parameters asn1.RawValue "optional" +} + +type RDNSequence []RelativeDistinguishedNameSET + +type RelativeDistinguishedNameSET []AttributeTypeAndValue + +type AttributeTypeAndValue struct { + Type asn1.ObjectIdentifier + Value interface{} +} + +// Extension represents the ASN.1 structure of the same name. See RFC +// 5280, section 4.2. +type Extension struct { + Id asn1.ObjectIdentifier + Critical bool "optional" + Value []byte +} + +// Name represents an X.509 distinguished name. This only includes the common +// elements of a DN. Additional elements in the name are ignored. +type Name struct { + Country, Organization, OrganizationalUnit []string + Locality, Province []string + StreetAddress, PostalCode []string + SerialNumber, CommonName string +} + +func (n *Name) FillFromRDNSequence(rdns *RDNSequence) { + for _, rdn := range *rdns { + if len(rdn) == 0 { + continue + } + atv := rdn[0] + value, ok := atv.Value.(string) + if !ok { + continue + } + + t := atv.Type + if len(t) == 4 && t[0] == 2 && t[1] == 5 && t[2] == 4 { + switch t[3] { + case 3: + n.CommonName = value + case 5: + n.SerialNumber = value + case 6: + n.Country = append(n.Country, value) + case 7: + n.Locality = append(n.Locality, value) + case 8: + n.Province = append(n.Province, value) + case 9: + n.StreetAddress = append(n.StreetAddress, value) + case 10: + n.Organization = append(n.Organization, value) + case 11: + n.OrganizationalUnit = append(n.OrganizationalUnit, value) + case 17: + n.PostalCode = append(n.PostalCode, value) + } + } + } +} + +var ( + oidCountry = []int{2, 5, 4, 6} + oidOrganization = []int{2, 5, 4, 10} + oidOrganizationalUnit = []int{2, 5, 4, 11} + oidCommonName = []int{2, 5, 4, 3} + oidSerialNumber = []int{2, 5, 4, 5} + oidLocality = []int{2, 5, 4, 7} + oidProvince = []int{2, 5, 4, 8} + oidStreetAddress = []int{2, 5, 4, 9} + oidPostalCode = []int{2, 5, 4, 17} +) + +// appendRDNs appends a relativeDistinguishedNameSET to the given RDNSequence +// and returns the new value. The relativeDistinguishedNameSET contains an +// attributeTypeAndValue for each of the given values. See RFC 5280, A.1, and +// search for AttributeTypeAndValue. +func appendRDNs(in RDNSequence, values []string, oid asn1.ObjectIdentifier) RDNSequence { + if len(values) == 0 { + return in + } + + s := make([]AttributeTypeAndValue, len(values)) + for i, value := range values { + s[i].Type = oid + s[i].Value = value + } + + return append(in, s) +} + +func (n Name) ToRDNSequence() (ret RDNSequence) { + ret = appendRDNs(ret, n.Country, oidCountry) + ret = appendRDNs(ret, n.Organization, oidOrganization) + ret = appendRDNs(ret, n.OrganizationalUnit, oidOrganizationalUnit) + ret = appendRDNs(ret, n.Locality, oidLocality) + ret = appendRDNs(ret, n.Province, oidProvince) + ret = appendRDNs(ret, n.StreetAddress, oidStreetAddress) + ret = appendRDNs(ret, n.PostalCode, oidPostalCode) + if len(n.CommonName) > 0 { + ret = appendRDNs(ret, []string{n.CommonName}, oidCommonName) + } + if len(n.SerialNumber) > 0 { + ret = appendRDNs(ret, []string{n.SerialNumber}, oidSerialNumber) + } + + return ret +} + +// CertificateList represents the ASN.1 structure of the same name. See RFC +// 5280, section 5.1. Use Certificate.CheckCRLSignature to verify the +// signature. +type CertificateList struct { + TBSCertList TBSCertificateList + SignatureAlgorithm AlgorithmIdentifier + SignatureValue asn1.BitString +} + +// HasExpired returns true iff currentTimeSeconds is past the expiry time of +// certList. +func (certList *CertificateList) HasExpired(currentTimeSeconds int64) bool { + return certList.TBSCertList.NextUpdate.Seconds() <= currentTimeSeconds +} + +// TBSCertificateList represents the ASN.1 structure of the same name. See RFC +// 5280, section 5.1. +type TBSCertificateList struct { + Raw asn1.RawContent + Version int "optional,default:2" + Signature AlgorithmIdentifier + Issuer RDNSequence + ThisUpdate *time.Time + NextUpdate *time.Time + RevokedCertificates []RevokedCertificate "optional" + Extensions []Extension "tag:0,optional,explicit" +} + +// RevokedCertificate represents the ASN.1 structure of the same name. See RFC +// 5280, section 5.1. +type RevokedCertificate struct { + SerialNumber *big.Int + RevocationTime *time.Time + Extensions []Extension "optional" +} diff --git a/src/pkg/crypto/x509/x509.go b/src/pkg/crypto/x509/x509.go index 6ae1f8e39..b10ffb0a2 100644 --- a/src/pkg/crypto/x509/x509.go +++ b/src/pkg/crypto/x509/x509.go @@ -14,7 +14,8 @@ import ( "crypto/dsa" "crypto/rsa" "crypto/sha1" - "crypto/x509/crl" + "crypto/x509/pkix" + "encoding/pem" "io" "os" "time" @@ -23,30 +24,25 @@ import ( // pkcs1PrivateKey is a structure which mirrors the PKCS#1 ASN.1 for an RSA private key. type pkcs1PrivateKey struct { Version int - N asn1.RawValue + N *big.Int E int - D asn1.RawValue - P asn1.RawValue - Q asn1.RawValue + D *big.Int + P *big.Int + Q *big.Int // We ignore these values, if present, because rsa will calculate them. - Dp asn1.RawValue "optional" - Dq asn1.RawValue "optional" - Qinv asn1.RawValue "optional" + Dp *big.Int "optional" + Dq *big.Int "optional" + Qinv *big.Int "optional" AdditionalPrimes []pkcs1AdditionalRSAPrime "optional" } type pkcs1AdditionalRSAPrime struct { - Prime asn1.RawValue + Prime *big.Int // We ignore these values because rsa will calculate them. - Exp asn1.RawValue - Coeff asn1.RawValue -} - -// rawValueIsInteger returns true iff the given ASN.1 RawValue is an INTEGER type. -func rawValueIsInteger(raw *asn1.RawValue) bool { - return raw.Class == 0 && raw.Tag == 2 && raw.IsCompound == false + Exp *big.Int + Coeff *big.Int } // ParsePKCS1PrivateKey returns an RSA private key from its ASN.1 PKCS#1 DER encoded form. @@ -65,29 +61,25 @@ func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err os.Error) { return nil, os.ErrorString("x509: unsupported private key version") } - if !rawValueIsInteger(&priv.N) || - !rawValueIsInteger(&priv.D) || - !rawValueIsInteger(&priv.P) || - !rawValueIsInteger(&priv.Q) { - err = asn1.StructuralError{"tags don't match"} - return + if priv.N.Sign() <= 0 || priv.D.Sign() <= 0 || priv.P.Sign() <= 0 || priv.Q.Sign() <= 0 { + return nil, os.ErrorString("private key contains zero or negative value") } key = new(rsa.PrivateKey) key.PublicKey = rsa.PublicKey{ E: priv.E, - N: new(big.Int).SetBytes(priv.N.Bytes), + N: priv.N, } - key.D = new(big.Int).SetBytes(priv.D.Bytes) + key.D = priv.D key.Primes = make([]*big.Int, 2+len(priv.AdditionalPrimes)) - key.Primes[0] = new(big.Int).SetBytes(priv.P.Bytes) - key.Primes[1] = new(big.Int).SetBytes(priv.Q.Bytes) + key.Primes[0] = priv.P + key.Primes[1] = priv.Q for i, a := range priv.AdditionalPrimes { - if !rawValueIsInteger(&a.Prime) { - return nil, asn1.StructuralError{"tags don't match"} + if a.Prime.Sign() <= 0 { + return nil, os.ErrorString("private key contains zero or negative prime") } - key.Primes[i+2] = new(big.Int).SetBytes(a.Prime.Bytes) + key.Primes[i+2] = a.Prime // We ignore the other two values because rsa will calculate // them as needed. } @@ -101,19 +93,6 @@ func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err os.Error) { return } -// rawValueForBig returns an asn1.RawValue which represents the given integer. -func rawValueForBig(n *big.Int) asn1.RawValue { - b := n.Bytes() - if n.Sign() >= 0 && len(b) > 0 && b[0]&0x80 != 0 { - // This positive number would be interpreted as a negative - // number in ASN.1 because the MSB is set. - padded := make([]byte, len(b)+1) - copy(padded[1:], b) - b = padded - } - return asn1.RawValue{Tag: 2, Bytes: b} -} - // MarshalPKCS1PrivateKey converts a private key to ASN.1 DER encoded form. func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte { key.Precompute() @@ -125,21 +104,21 @@ func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte { priv := pkcs1PrivateKey{ Version: version, - N: rawValueForBig(key.N), + N: key.N, E: key.PublicKey.E, - D: rawValueForBig(key.D), - P: rawValueForBig(key.Primes[0]), - Q: rawValueForBig(key.Primes[1]), - Dp: rawValueForBig(key.Precomputed.Dp), - Dq: rawValueForBig(key.Precomputed.Dq), - Qinv: rawValueForBig(key.Precomputed.Qinv), + D: key.D, + P: key.Primes[0], + Q: key.Primes[1], + Dp: key.Precomputed.Dp, + Dq: key.Precomputed.Dq, + Qinv: key.Precomputed.Qinv, } priv.AdditionalPrimes = make([]pkcs1AdditionalRSAPrime, len(key.Precomputed.CRTValues)) for i, values := range key.Precomputed.CRTValues { - priv.AdditionalPrimes[i].Prime = rawValueForBig(key.Primes[2+i]) - priv.AdditionalPrimes[i].Exp = rawValueForBig(values.Exp) - priv.AdditionalPrimes[i].Coeff = rawValueForBig(values.Coeff) + priv.AdditionalPrimes[i].Prime = key.Primes[2+i] + priv.AdditionalPrimes[i].Exp = values.Exp + priv.AdditionalPrimes[i].Coeff = values.Coeff } b, _ := asn1.Marshal(priv) @@ -151,44 +130,30 @@ func MarshalPKCS1PrivateKey(key *rsa.PrivateKey) []byte { type certificate struct { Raw asn1.RawContent TBSCertificate tbsCertificate - SignatureAlgorithm algorithmIdentifier + SignatureAlgorithm pkix.AlgorithmIdentifier SignatureValue asn1.BitString } type tbsCertificate struct { Raw asn1.RawContent Version int "optional,explicit,default:1,tag:0" - SerialNumber asn1.RawValue - SignatureAlgorithm algorithmIdentifier - Issuer rdnSequence + SerialNumber *big.Int + SignatureAlgorithm pkix.AlgorithmIdentifier + Issuer pkix.RDNSequence Validity validity - Subject rdnSequence + Subject pkix.RDNSequence PublicKey publicKeyInfo - UniqueId asn1.BitString "optional,tag:1" - SubjectUniqueId asn1.BitString "optional,tag:2" - Extensions []extension "optional,explicit,tag:3" + UniqueId asn1.BitString "optional,tag:1" + SubjectUniqueId asn1.BitString "optional,tag:2" + Extensions []pkix.Extension "optional,explicit,tag:3" } type dsaAlgorithmParameters struct { - P, Q, G asn1.RawValue + P, Q, G *big.Int } type dsaSignature struct { - R, S asn1.RawValue -} - -type algorithmIdentifier struct { - Algorithm asn1.ObjectIdentifier - Parameters asn1.RawValue "optional" -} - -type rdnSequence []relativeDistinguishedNameSET - -type relativeDistinguishedNameSET []attributeTypeAndValue - -type attributeTypeAndValue struct { - Type asn1.ObjectIdentifier - Value interface{} + R, S *big.Int } type validity struct { @@ -197,16 +162,10 @@ type validity struct { type publicKeyInfo struct { Raw asn1.RawContent - Algorithm algorithmIdentifier + Algorithm pkix.AlgorithmIdentifier PublicKey asn1.BitString } -type extension struct { - Id asn1.ObjectIdentifier - Critical bool "optional" - Value []byte -} - // RFC 5280, 4.2.1.1 type authKeyId struct { Id []byte "optional,tag:0" @@ -234,100 +193,6 @@ const ( DSA ) -// Name represents an X.509 distinguished name. This only includes the common -// elements of a DN. Additional elements in the name are ignored. -type Name struct { - Country, Organization, OrganizationalUnit []string - Locality, Province []string - StreetAddress, PostalCode []string - SerialNumber, CommonName string -} - -func (n *Name) fillFromRDNSequence(rdns *rdnSequence) { - for _, rdn := range *rdns { - if len(rdn) == 0 { - continue - } - atv := rdn[0] - value, ok := atv.Value.(string) - if !ok { - continue - } - - t := atv.Type - if len(t) == 4 && t[0] == 2 && t[1] == 5 && t[2] == 4 { - switch t[3] { - case 3: - n.CommonName = value - case 5: - n.SerialNumber = value - case 6: - n.Country = append(n.Country, value) - case 7: - n.Locality = append(n.Locality, value) - case 8: - n.Province = append(n.Province, value) - case 9: - n.StreetAddress = append(n.StreetAddress, value) - case 10: - n.Organization = append(n.Organization, value) - case 11: - n.OrganizationalUnit = append(n.OrganizationalUnit, value) - case 17: - n.PostalCode = append(n.PostalCode, value) - } - } - } -} - -var ( - oidCountry = []int{2, 5, 4, 6} - oidOrganization = []int{2, 5, 4, 10} - oidOrganizationalUnit = []int{2, 5, 4, 11} - oidCommonName = []int{2, 5, 4, 3} - oidSerialNumber = []int{2, 5, 4, 5} - oidLocality = []int{2, 5, 4, 7} - oidProvince = []int{2, 5, 4, 8} - oidStreetAddress = []int{2, 5, 4, 9} - oidPostalCode = []int{2, 5, 4, 17} -) - -// appendRDNs appends a relativeDistinguishedNameSET to the given rdnSequence -// and returns the new value. The relativeDistinguishedNameSET contains an -// attributeTypeAndValue for each of the given values. See RFC 5280, A.1, and -// search for AttributeTypeAndValue. -func appendRDNs(in rdnSequence, values []string, oid asn1.ObjectIdentifier) rdnSequence { - if len(values) == 0 { - return in - } - - s := make([]attributeTypeAndValue, len(values)) - for i, value := range values { - s[i].Type = oid - s[i].Value = value - } - - return append(in, s) -} - -func (n Name) toRDNSequence() (ret rdnSequence) { - ret = appendRDNs(ret, n.Country, oidCountry) - ret = appendRDNs(ret, n.Organization, oidOrganization) - ret = appendRDNs(ret, n.OrganizationalUnit, oidOrganizationalUnit) - ret = appendRDNs(ret, n.Locality, oidLocality) - ret = appendRDNs(ret, n.Province, oidProvince) - ret = appendRDNs(ret, n.StreetAddress, oidStreetAddress) - ret = appendRDNs(ret, n.PostalCode, oidPostalCode) - if len(n.CommonName) > 0 { - ret = appendRDNs(ret, []string{n.CommonName}, oidCommonName) - } - if len(n.SerialNumber) > 0 { - ret = appendRDNs(ret, []string{n.SerialNumber}, oidSerialNumber) - } - - return ret -} - // OIDs for signature algorithms // // pkcs-1 OBJECT IDENTIFIER ::= { @@ -483,9 +348,9 @@ type Certificate struct { PublicKey interface{} Version int - SerialNumber []byte - Issuer Name - Subject Name + SerialNumber *big.Int + Issuer pkix.Name + Subject pkix.Name NotBefore, NotAfter *time.Time // Validity bounds. KeyUsage KeyUsage @@ -591,12 +456,10 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature if _, err := asn1.Unmarshal(signature, dsaSig); err != nil { return err } - if !rawValueIsInteger(&dsaSig.R) || !rawValueIsInteger(&dsaSig.S) { - return asn1.StructuralError{"tags don't match"} + if dsaSig.R.Sign() <= 0 || dsaSig.S.Sign() <= 0 { + return os.ErrorString("DSA signature contained zero or negative values") } - r := new(big.Int).SetBytes(dsaSig.R.Bytes) - s := new(big.Int).SetBytes(dsaSig.S.Bytes) - if !dsa.Verify(pub, digest, r, s) { + if !dsa.Verify(pub, digest, dsaSig.R, dsaSig.S) { return os.ErrorString("DSA verification failure") } return @@ -605,8 +468,8 @@ func (c *Certificate) CheckSignature(algo SignatureAlgorithm, signed, signature } // CheckCRLSignature checks that the signature in crl is from c. -func (c *Certificate) CheckCRLSignature(crl *crl.CertificateList) (err os.Error) { - algo := getSignatureAlgorithmFromOID(crl.SignatureAlgorithm.Algo) +func (c *Certificate) CheckCRLSignature(crl *pkix.CertificateList) (err os.Error) { + algo := getSignatureAlgorithmFromOID(crl.SignatureAlgorithm.Algorithm) return c.CheckSignature(algo, crl.TBSCertList.Raw, crl.SignatureValue.RightAlign()) } @@ -622,7 +485,7 @@ type basicConstraints struct { } type rsaPublicKey struct { - N asn1.RawValue + N *big.Int E int } @@ -654,42 +517,33 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{ return nil, err } - if !rawValueIsInteger(&p.N) { - return nil, asn1.StructuralError{"tags don't match"} - } - pub := &rsa.PublicKey{ E: p.E, - N: new(big.Int).SetBytes(p.N.Bytes), + N: p.N, } return pub, nil case DSA: - p := new(asn1.RawValue) - _, err := asn1.Unmarshal(asn1Data, p) + var p *big.Int + _, err := asn1.Unmarshal(asn1Data, &p) if err != nil { return nil, err } - if !rawValueIsInteger(p) { - return nil, asn1.StructuralError{"tags don't match"} - } paramsData := keyData.Algorithm.Parameters.FullBytes params := new(dsaAlgorithmParameters) _, err = asn1.Unmarshal(paramsData, params) if err != nil { return nil, err } - if !rawValueIsInteger(¶ms.P) || - !rawValueIsInteger(¶ms.Q) || - !rawValueIsInteger(¶ms.G) { - return nil, asn1.StructuralError{"tags don't match"} + if p.Sign() <= 0 || params.P.Sign() <= 0 || params.Q.Sign() <= 0 || params.G.Sign() <= 0 { + return nil, os.ErrorString("zero or negative DSA parameter") } pub := &dsa.PublicKey{ Parameters: dsa.Parameters{ - P: new(big.Int).SetBytes(params.P.Bytes), - Q: new(big.Int).SetBytes(params.Q.Bytes), - G: new(big.Int).SetBytes(params.G.Bytes), + P: params.P, + Q: params.Q, + G: params.G, }, - Y: new(big.Int).SetBytes(p.Bytes), + Y: p, } return pub, nil default: @@ -716,10 +570,14 @@ func parseCertificate(in *certificate) (*Certificate, os.Error) { return nil, err } + if in.TBSCertificate.SerialNumber.Sign() < 0 { + return nil, os.ErrorString("negative serial number") + } + out.Version = in.TBSCertificate.Version + 1 - out.SerialNumber = in.TBSCertificate.SerialNumber.Bytes - out.Issuer.fillFromRDNSequence(&in.TBSCertificate.Issuer) - out.Subject.fillFromRDNSequence(&in.TBSCertificate.Subject) + out.SerialNumber = in.TBSCertificate.SerialNumber + out.Issuer.FillFromRDNSequence(&in.TBSCertificate.Issuer) + out.Subject.FillFromRDNSequence(&in.TBSCertificate.Subject) out.NotBefore = in.TBSCertificate.Validity.NotBefore out.NotAfter = in.TBSCertificate.Validity.NotAfter @@ -977,8 +835,8 @@ var ( oidExtensionNameConstraints = []int{2, 5, 29, 30} ) -func buildExtensions(template *Certificate) (ret []extension, err os.Error) { - ret = make([]extension, 7 /* maximum number of elements. */ ) +func buildExtensions(template *Certificate) (ret []pkix.Extension, err os.Error) { + ret = make([]pkix.Extension, 7 /* maximum number of elements. */ ) n := 0 if template.KeyUsage != 0 { @@ -1095,7 +953,7 @@ var ( // The returned slice is the certificate in DER encoding. func CreateCertificate(rand io.Reader, template, parent *Certificate, pub *rsa.PublicKey, priv *rsa.PrivateKey) (cert []byte, err os.Error) { asn1PublicKey, err := asn1.Marshal(rsaPublicKey{ - N: asn1.RawValue{Tag: 2, Bytes: pub.N.Bytes()}, + N: pub.N, E: pub.E, }) if err != nil { @@ -1114,12 +972,12 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub *rsa.P encodedPublicKey := asn1.BitString{BitLength: len(asn1PublicKey) * 8, Bytes: asn1PublicKey} c := tbsCertificate{ Version: 2, - SerialNumber: asn1.RawValue{Bytes: template.SerialNumber, Tag: 2}, - SignatureAlgorithm: algorithmIdentifier{Algorithm: oidSHA1WithRSA}, - Issuer: parent.Subject.toRDNSequence(), + SerialNumber: template.SerialNumber, + SignatureAlgorithm: pkix.AlgorithmIdentifier{Algorithm: oidSHA1WithRSA}, + Issuer: parent.Subject.ToRDNSequence(), Validity: validity{template.NotBefore, template.NotAfter}, - Subject: template.Subject.toRDNSequence(), - PublicKey: publicKeyInfo{nil, algorithmIdentifier{Algorithm: oidRSA}, encodedPublicKey}, + Subject: template.Subject.ToRDNSequence(), + PublicKey: publicKeyInfo{nil, pkix.AlgorithmIdentifier{Algorithm: oidRSA}, encodedPublicKey}, Extensions: extensions, } @@ -1142,8 +1000,75 @@ func CreateCertificate(rand io.Reader, template, parent *Certificate, pub *rsa.P cert, err = asn1.Marshal(certificate{ nil, c, - algorithmIdentifier{Algorithm: oidSHA1WithRSA}, + pkix.AlgorithmIdentifier{Algorithm: oidSHA1WithRSA}, asn1.BitString{Bytes: signature, BitLength: len(signature) * 8}, }) return } + +// pemCRLPrefix is the magic string that indicates that we have a PEM encoded +// CRL. +var pemCRLPrefix = []byte("-----BEGIN X509 CRL") +// pemType is the type of a PEM encoded CRL. +var pemType = "X509 CRL" + +// ParseCRL parses a CRL from the given bytes. It's often the case that PEM +// encoded CRLs will appear where they should be DER encoded, so this function +// will transparently handle PEM encoding as long as there isn't any leading +// garbage. +func ParseCRL(crlBytes []byte) (certList *pkix.CertificateList, err os.Error) { + if bytes.HasPrefix(crlBytes, pemCRLPrefix) { + block, _ := pem.Decode(crlBytes) + if block != nil && block.Type == pemType { + crlBytes = block.Bytes + } + } + return ParseDERCRL(crlBytes) +} + +// ParseDERCRL parses a DER encoded CRL from the given bytes. +func ParseDERCRL(derBytes []byte) (certList *pkix.CertificateList, err os.Error) { + certList = new(pkix.CertificateList) + _, err = asn1.Unmarshal(derBytes, certList) + if err != nil { + certList = nil + } + return +} + +// CreateCRL returns a DER encoded CRL, signed by this Certificate, that +// contains the given list of revoked certificates. +func (c *Certificate) CreateCRL(rand io.Reader, priv *rsa.PrivateKey, revokedCerts []pkix.RevokedCertificate, now, expiry *time.Time) (crlBytes []byte, err os.Error) { + tbsCertList := pkix.TBSCertificateList{ + Version: 2, + Signature: pkix.AlgorithmIdentifier{ + Algorithm: oidSignatureSHA1WithRSA, + }, + Issuer: c.Subject.ToRDNSequence(), + ThisUpdate: now, + NextUpdate: expiry, + RevokedCertificates: revokedCerts, + } + + tbsCertListContents, err := asn1.Marshal(tbsCertList) + if err != nil { + return + } + + h := sha1.New() + h.Write(tbsCertListContents) + digest := h.Sum() + + signature, err := rsa.SignPKCS1v15(rand, priv, crypto.SHA1, digest) + if err != nil { + return + } + + return asn1.Marshal(pkix.CertificateList{ + TBSCertList: tbsCertList, + SignatureAlgorithm: pkix.AlgorithmIdentifier{ + Algorithm: oidSignatureSHA1WithRSA, + }, + SignatureValue: asn1.BitString{Bytes: signature, BitLength: len(signature) * 8}, + }) +} diff --git a/src/pkg/crypto/x509/x509_test.go b/src/pkg/crypto/x509/x509_test.go index fd137a6f5..dc216505e 100644 --- a/src/pkg/crypto/x509/x509_test.go +++ b/src/pkg/crypto/x509/x509_test.go @@ -10,6 +10,8 @@ import ( "crypto/dsa" "crypto/rand" "crypto/rsa" + "crypto/x509/pkix" + "encoding/base64" "encoding/hex" "encoding/pem" "testing" @@ -207,8 +209,8 @@ func TestCreateSelfSignedCertificate(t *testing.T) { } template := Certificate{ - SerialNumber: []byte{1}, - Subject: Name{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ CommonName: "test.example.com", Organization: []string{"Acme Co"}, }, @@ -331,3 +333,99 @@ func TestVerifyCertificateWithDSASignature(t *testing.T) { t.Fatalf("DSA Certificate verfication failed: %s", err) } } + +const pemCertificate = `-----BEGIN CERTIFICATE----- +MIIB5DCCAZCgAwIBAgIBATALBgkqhkiG9w0BAQUwLTEQMA4GA1UEChMHQWNtZSBDbzEZMBcGA1UE +AxMQdGVzdC5leGFtcGxlLmNvbTAeFw03MDAxMDEwMDE2NDBaFw03MDAxMDIwMzQ2NDBaMC0xEDAO +BgNVBAoTB0FjbWUgQ28xGTAXBgNVBAMTEHRlc3QuZXhhbXBsZS5jb20wWjALBgkqhkiG9w0BAQED +SwAwSAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0fd7Ai2KW5ToIwzFo +fvJcS/STa6HA5gQenRUCAwEAAaOBnjCBmzAOBgNVHQ8BAf8EBAMCAAQwDwYDVR0TAQH/BAUwAwEB +/zANBgNVHQ4EBgQEAQIDBDAPBgNVHSMECDAGgAQBAgMEMBsGA1UdEQQUMBKCEHRlc3QuZXhhbXBs +ZS5jb20wDwYDVR0gBAgwBjAEBgIqAzAqBgNVHR4EIzAhoB8wDoIMLmV4YW1wbGUuY29tMA2CC2V4 +YW1wbGUuY29tMAsGCSqGSIb3DQEBBQNBAHKZKoS1wEQOGhgklx4+/yFYQlnqwKXvar/ZecQvJwui +0seMQnwBhwdBkHfVIU2Fu5VUMRyxlf0ZNaDXcpU581k= +-----END CERTIFICATE-----` + +func TestCRLCreation(t *testing.T) { + block, _ := pem.Decode([]byte(pemPrivateKey)) + priv, _ := ParsePKCS1PrivateKey(block.Bytes) + block, _ = pem.Decode([]byte(pemCertificate)) + cert, _ := ParseCertificate(block.Bytes) + + now := time.SecondsToUTC(1000) + expiry := time.SecondsToUTC(10000) + + revokedCerts := []pkix.RevokedCertificate{ + { + SerialNumber: big.NewInt(1), + RevocationTime: now, + }, + { + SerialNumber: big.NewInt(42), + RevocationTime: now, + }, + } + + crlBytes, err := cert.CreateCRL(rand.Reader, priv, revokedCerts, now, expiry) + if err != nil { + t.Errorf("error creating CRL: %s", err) + } + + _, err = ParseDERCRL(crlBytes) + if err != nil { + t.Errorf("error reparsing CRL: %s", err) + } +} + +func fromBase64(in string) []byte { + out := make([]byte, base64.StdEncoding.DecodedLen(len(in))) + _, err := base64.StdEncoding.Decode(out, []byte(in)) + if err != nil { + panic("failed to base64 decode") + } + return out +} + +func TestParseDERCRL(t *testing.T) { + derBytes := fromBase64(derCRLBase64) + certList, err := ParseDERCRL(derBytes) + if err != nil { + t.Errorf("error parsing: %s", err) + return + } + numCerts := len(certList.TBSCertList.RevokedCertificates) + expected := 88 + if numCerts != expected { + t.Errorf("bad number of revoked certificates. got: %d want: %d", numCerts, expected) + } + + if certList.HasExpired(1302517272) { + t.Errorf("CRL has expired (but shouldn't have)") + } + + // Can't check the signature here without a package cycle. +} + +func TestParsePEMCRL(t *testing.T) { + pemBytes := fromBase64(pemCRLBase64) + certList, err := ParseCRL(pemBytes) + if err != nil { + t.Errorf("error parsing: %s", err) + return + } + numCerts := len(certList.TBSCertList.RevokedCertificates) + expected := 2 + if numCerts != expected { + t.Errorf("bad number of revoked certificates. got: %d want: %d", numCerts, expected) + } + + if certList.HasExpired(1302517272) { + t.Errorf("CRL has expired (but shouldn't have)") + } + + // Can't check the signature here without a package cycle. +} + +const derCRLBase64 = "MIINqzCCDJMCAQEwDQYJKoZIhvcNAQEFBQAwVjEZMBcGA1UEAxMQUEtJIEZJTk1FQ0NBTklDQTEVMBMGA1UEChMMRklOTUVDQ0FOSUNBMRUwEwYDVQQLEwxGSU5NRUNDQU5JQ0ExCzAJBgNVBAYTAklUFw0xMTA1MDQxNjU3NDJaFw0xMTA1MDQyMDU3NDJaMIIMBzAhAg4Ze1od49Lt1qIXBydAzhcNMDkwNzE2MDg0MzIyWjAAMCECDl0HSL9bcZ1Ci/UHJ0DPFw0wOTA3MTYwODQzMTNaMAAwIQIOESB9tVAmX3cY7QcnQNAXDTA5MDcxNjA4NDUyMlowADAhAg4S1tGAQ3mHt8uVBydA1RcNMDkwODA0MTUyNTIyWjAAMCECDlQ249Y7vtC25ScHJ0DWFw0wOTA4MDQxNTI1MzdaMAAwIQIOISMop3NkA4PfYwcnQNkXDTA5MDgwNDExMDAzNFowADAhAg56/BMoS29KEShTBydA2hcNMDkwODA0MTEwMTAzWjAAMCECDnBp/22HPH5CSWoHJ0DbFw0wOTA4MDQxMDU0NDlaMAAwIQIOV9IP+8CD8bK+XAcnQNwXDTA5MDgwNDEwNTcxN1owADAhAg4v5aRz0IxWqYiXBydA3RcNMDkwODA0MTA1NzQ1WjAAMCECDlOU34VzvZAybQwHJ0DeFw0wOTA4MDQxMDU4MjFaMAAwIAINO4CD9lluIxcwBydBAxcNMDkwNzIyMTUzMTU5WjAAMCECDgOllfO8Y1QA7/wHJ0ExFw0wOTA3MjQxMTQxNDNaMAAwIQIOJBX7jbiCdRdyjgcnQUQXDTA5MDkxNjA5MzAwOFowADAhAg5iYSAgmDrlH/RZBydBRRcNMDkwOTE2MDkzMDE3WjAAMCECDmu6k6srP3jcMaQHJ0FRFw0wOTA4MDQxMDU2NDBaMAAwIQIOX8aHlO0V+WVH4QcnQVMXDTA5MDgwNDEwNTcyOVowADAhAg5flK2rg3NnsRgDBydBzhcNMTEwMjAxMTUzMzQ2WjAAMCECDg35yJDL1jOPTgoHJ0HPFw0xMTAyMDExNTM0MjZaMAAwIQIOMyFJ6+e9iiGVBQcnQdAXDTA5MDkxODEzMjAwNVowADAhAg5Emb/Oykucmn8fBydB1xcNMDkwOTIxMTAxMDQ3WjAAMCECDjQKCncV+MnUavMHJ0HaFw0wOTA5MjIwODE1MjZaMAAwIQIOaxiFUt3dpd+tPwcnQfQXDTEwMDYxODA4NDI1MVowADAhAg5G7P8nO0tkrMt7BydB9RcNMTAwNjE4MDg0MjMwWjAAMCECDmTCC3SXhmDRst4HJ0H2Fw0wOTA5MjgxMjA3MjBaMAAwIQIOHoGhUr/pRwzTKgcnQfcXDTA5MDkyODEyMDcyNFowADAhAg50wrcrCiw8mQmPBydCBBcNMTAwMjE2MTMwMTA2WjAAMCECDifWmkvwyhEqwEcHJ0IFFw0xMDAyMTYxMzAxMjBaMAAwIQIOfgPmlW9fg+osNgcnQhwXDTEwMDQxMzA5NTIwMFowADAhAg4YHAGuA6LgCk7tBydCHRcNMTAwNDEzMDk1MTM4WjAAMCECDi1zH1bxkNJhokAHJ0IsFw0xMDA0MTMwOTU5MzBaMAAwIQIOMipNccsb/wo2fwcnQi0XDTEwMDQxMzA5NTkwMFowADAhAg46lCmvPl4GpP6ABydCShcNMTAwMTE5MDk1MjE3WjAAMCECDjaTcaj+wBpcGAsHJ0JLFw0xMDAxMTkwOTUyMzRaMAAwIQIOOMC13EOrBuxIOQcnQloXDTEwMDIwMTA5NDcwNVowADAhAg5KmZl+krz4RsmrBydCWxcNMTAwMjAxMDk0NjQwWjAAMCECDmLG3zQJ/fzdSsUHJ0JiFw0xMDAzMDEwOTUxNDBaMAAwIQIOP39ksgHdojf4owcnQmMXDTEwMDMwMTA5NTExN1owADAhAg4LDQzvWNRlD6v9BydCZBcNMTAwMzAxMDk0NjIyWjAAMCECDkmNfeclaFhIaaUHJ0JlFw0xMDAzMDEwOTQ2MDVaMAAwIQIOT/qWWfpH/m8NTwcnQpQXDTEwMDUxMTA5MTgyMVowADAhAg5m/ksYxvCEgJSvBydClRcNMTAwNTExMDkxODAxWjAAMCECDgvf3Ohq6JOPU9AHJ0KWFw0xMDA1MTEwOTIxMjNaMAAwIQIOKSPas10z4jNVIQcnQpcXDTEwMDUxMTA5MjEwMlowADAhAg4mCWmhoZ3lyKCDBydCohcNMTEwNDI4MTEwMjI1WjAAMCECDkeiyRsBMK0Gvr4HJ0KjFw0xMTA0MjgxMTAyMDdaMAAwIQIOa09b/nH2+55SSwcnQq4XDTExMDQwMTA4Mjk0NlowADAhAg5O7M7iq7gGplr1BydCrxcNMTEwNDAxMDgzMDE3WjAAMCECDjlT6mJxUjTvyogHJ0K1Fw0xMTAxMjcxNTQ4NTJaMAAwIQIODS/l4UUFLe21NAcnQrYXDTExMDEyNzE1NDgyOFowADAhAg5lPRA0XdOUF6lSBydDHhcNMTEwMTI4MTQzNTA1WjAAMCECDixKX4fFGGpENwgHJ0MfFw0xMTAxMjgxNDM1MzBaMAAwIQIORNBkqsPnpKTtbAcnQ08XDTEwMDkwOTA4NDg0MlowADAhAg5QL+EMM3lohedEBydDUBcNMTAwOTA5MDg0ODE5WjAAMCECDlhDnHK+HiTRAXcHJ0NUFw0xMDEwMTkxNjIxNDBaMAAwIQIOdBFqAzq/INz53gcnQ1UXDTEwMTAxOTE2MjA0NFowADAhAg4OjR7s8MgKles1BydDWhcNMTEwMTI3MTY1MzM2WjAAMCECDmfR/elHee+d0SoHJ0NbFw0xMTAxMjcxNjUzNTZaMAAwIQIOBTKv2ui+KFMI+wcnQ5YXDTEwMDkxNTEwMjE1N1owADAhAg49F3c/GSah+oRUBydDmxcNMTEwMTI3MTczMjMzWjAAMCECDggv4I61WwpKFMMHJ0OcFw0xMTAxMjcxNzMyNTVaMAAwIQIOXx/Y8sEvwS10LAcnQ6UXDTExMDEyODExMjkzN1owADAhAg5LSLbnVrSKaw/9BydDphcNMTEwMTI4MTEyOTIwWjAAMCECDmFFoCuhKUeACQQHJ0PfFw0xMTAxMTExMDE3MzdaMAAwIQIOQTDdFh2fSPF6AAcnQ+AXDTExMDExMTEwMTcxMFowADAhAg5B8AOXX61FpvbbBydD5RcNMTAxMDA2MTAxNDM2WjAAMCECDh41P2Gmi7PkwI4HJ0PmFw0xMDEwMDYxMDE2MjVaMAAwIQIOWUHGLQCd+Ale9gcnQ/0XDTExMDUwMjA3NTYxMFowADAhAg5Z2c9AYkikmgWOBydD/hcNMTEwNTAyMDc1NjM0WjAAMCECDmf/UD+/h8nf+74HJ0QVFw0xMTA0MTUwNzI4MzNaMAAwIQIOICvj4epy3MrqfwcnRBYXDTExMDQxNTA3Mjg1NlowADAhAg4bouRMfOYqgv4xBydEHxcNMTEwMzA4MTYyNDI1WjAAMCECDhebWHGoKiTp7pEHJ0QgFw0xMTAzMDgxNjI0NDhaMAAwIQIOX+qnxxAqJ8LtawcnRDcXDTExMDEzMTE1MTIyOFowADAhAg4j0fICqZ+wkOdqBydEOBcNMTEwMTMxMTUxMTQxWjAAMCECDhmXjsV4SUpWtAMHJ0RLFw0xMTAxMjgxMTI0MTJaMAAwIQIODno/w+zG43kkTwcnREwXDTExMDEyODExMjM1MlowADAhAg4b1gc88767Fr+LBydETxcNMTEwMTI4MTEwMjA4WjAAMCECDn+M3Pa1w2nyFeUHJ0RQFw0xMTAxMjgxMDU4NDVaMAAwIQIOaduoyIH61tqybAcnRJUXDTEwMTIxNTA5NDMyMlowADAhAg4nLqQPkyi3ESAKBydElhcNMTAxMjE1MDk0MzM2WjAAMCECDi504NIMH8578gQHJ0SbFw0xMTAyMTQxNDA1NDFaMAAwIQIOGuaM8PDaC5u1egcnRJwXDTExMDIxNDE0MDYwNFowADAhAg4ehYq/BXGnB5PWBydEnxcNMTEwMjA0MDgwOTUxWjAAMCECDkSD4eS4FxW5H20HJ0SgFw0xMTAyMDQwODA5MjVaMAAwIQIOOCcb6ilYObt1egcnRKEXDTExMDEyNjEwNDEyOVowADAhAg58tISWCCwFnKGnBydEohcNMTEwMjA0MDgxMzQyWjAAMCECDn5rjtabY/L/WL0HJ0TJFw0xMTAyMDQxMTAzNDFaMAAwDQYJKoZIhvcNAQEFBQADggEBAGnF2Gs0+LNiYCW1Ipm83OXQYP/bd5tFFRzyz3iepFqNfYs4D68/QihjFoRHQoXEB0OEe1tvaVnnPGnEOpi6krwekquMxo4H88B5SlyiFIqemCOIss0SxlCFs69LmfRYvPPvPEhoXtQ3ZThe0UvKG83GOklhvGl6OaiRf4Mt+m8zOT4Wox/j6aOBK6cw6qKCdmD+Yj1rrNqFGg1CnSWMoD6S6mwNgkzwdBUJZ22BwrzAAo4RHa2Uy3ef1FjwD0XtU5N3uDSxGGBEDvOe5z82rps3E22FpAA8eYl8kaXtmWqyvYU0epp4brGuTxCuBMCAsxt/OjIjeNNQbBGkwxgfYA0=" + +const pemCRLBase64 = "LS0tLS1CRUdJTiBYNTA5IENSTC0tLS0tDQpNSUlCOWpDQ0FWOENBUUV3RFFZSktvWklodmNOQVFFRkJRQXdiREVhTUJnR0ExVUVDaE1SVWxOQklGTmxZM1Z5DQphWFI1SUVsdVl5NHhIakFjQmdOVkJBTVRGVkpUUVNCUWRXSnNhV01nVW05dmRDQkRRU0IyTVRFdU1Dd0dDU3FHDQpTSWIzRFFFSkFSWWZjbk5oYTJWdmJuSnZiM1J6YVdkdVFISnpZWE5sWTNWeWFYUjVMbU52YlJjTk1URXdNakl6DQpNVGt5T0RNd1doY05NVEV3T0RJeU1Ua3lPRE13V2pDQmpEQktBaEVBckRxb2g5RkhKSFhUN09QZ3V1bjQrQmNODQpNRGt4TVRBeU1UUXlOekE1V2pBbU1Bb0dBMVVkRlFRRENnRUpNQmdHQTFVZEdBUVJHQTh5TURBNU1URXdNakUwDQpNalExTlZvd1BnSVJBTEd6blowOTVQQjVhQU9MUGc1N2ZNTVhEVEF5TVRBeU16RTBOVEF4TkZvd0dqQVlCZ05WDQpIUmdFRVJnUE1qQXdNakV3TWpNeE5EVXdNVFJhb0RBd0xqQWZCZ05WSFNNRUdEQVdnQlQxVERGNlVRTS9MTmVMDQpsNWx2cUhHUXEzZzltekFMQmdOVkhSUUVCQUlDQUlRd0RRWUpLb1pJaHZjTkFRRUZCUUFEZ1lFQUZVNUFzNk16DQpxNVBSc2lmYW9iUVBHaDFhSkx5QytNczVBZ2MwYld5QTNHQWR4dXI1U3BQWmVSV0NCamlQL01FSEJXSkNsQkhQDQpHUmNxNXlJZDNFakRrYUV5eFJhK2k2N0x6dmhJNmMyOUVlNks5cFNZd2ppLzdSVWhtbW5Qclh0VHhsTDBsckxyDQptUVFKNnhoRFJhNUczUUE0Q21VZHNITnZicnpnbUNZcHZWRT0NCi0tLS0tRU5EIFg1MDkgQ1JMLS0tLS0NCg0K" diff --git a/src/pkg/ebnf/ebnf.go b/src/pkg/ebnf/ebnf.go index 964e1c1b0..661afdd35 100644 --- a/src/pkg/ebnf/ebnf.go +++ b/src/pkg/ebnf/ebnf.go @@ -8,7 +8,7 @@ // Production = name "=" [ Expression ] "." . // Expression = Alternative { "|" Alternative } . // Alternative = Term { Term } . -// Term = name | token [ "..." token ] | Group | Option | Repetition . +// Term = name | token [ "…" token ] | Group | Option | Repetition . // Group = "(" Expression ")" . // Option = "[" Expression "]" . // Repetition = "{" Expression "}" . @@ -82,6 +82,12 @@ type ( Body Expression // {body} } + // A Bad node stands for pieces of source code that lead to a parse error. + Bad struct { + TokPos token.Pos + Error string // parser error message + } + // A Production node represents an EBNF production. Production struct { Name *Name @@ -103,6 +109,7 @@ func (x *Range) Pos() token.Pos { return x.Begin.Pos() } func (x *Group) Pos() token.Pos { return x.Lparen } func (x *Option) Pos() token.Pos { return x.Lbrack } func (x *Repetition) Pos() token.Pos { return x.Lbrace } +func (x *Bad) Pos() token.Pos { return x.TokPos } func (x *Production) Pos() token.Pos { return x.Name.Pos() } diff --git a/src/pkg/ebnf/ebnf_test.go b/src/pkg/ebnf/ebnf_test.go index 2055f872a..30301748d 100644 --- a/src/pkg/ebnf/ebnf_test.go +++ b/src/pkg/ebnf/ebnf_test.go @@ -14,7 +14,7 @@ import ( var fset = token.NewFileSet() -var grammars = []string{ +var goodGrammars = []string{ `Program = .`, `Program = foo . @@ -38,7 +38,19 @@ var grammars = []string{ } -func check(t *testing.T, filename string, src []byte) { +var badGrammars = []string{ + `Program = | .`, + `Program = | b .`, + `Program = a … b .`, + `Program = "a" … .`, + `Program = … "b" .`, + `Program = () .`, + `Program = [] .`, + `Program = {} .`, +} + + +func checkGood(t *testing.T, filename string, src []byte) { grammar, err := Parse(fset, filename, src) if err != nil { t.Errorf("Parse(%s) failed: %v", src, err) @@ -49,9 +61,20 @@ func check(t *testing.T, filename string, src []byte) { } +func checkBad(t *testing.T, filename string, src []byte) { + _, err := Parse(fset, filename, src) + if err == nil { + t.Errorf("Parse(%s) should have failed", src) + } +} + + func TestGrammars(t *testing.T) { - for _, src := range grammars { - check(t, "", []byte(src)) + for _, src := range goodGrammars { + checkGood(t, "", []byte(src)) + } + for _, src := range badGrammars { + checkBad(t, "", []byte(src)) } } @@ -67,6 +90,6 @@ func TestFiles(t *testing.T) { if err != nil { t.Fatal(err) } - check(t, filename, src) + checkGood(t, filename, src) } } diff --git a/src/pkg/ebnf/parser.go b/src/pkg/ebnf/parser.go index 166412f99..ede4f7073 100644 --- a/src/pkg/ebnf/parser.go +++ b/src/pkg/ebnf/parser.go @@ -85,6 +85,7 @@ func (p *parser) parseToken() *Token { } +// ParseTerm returns nil if no term was found. func (p *parser) parseTerm() (x Expression) { pos := p.pos @@ -131,7 +132,8 @@ func (p *parser) parseSequence() Expression { // no need for a sequence if list.Len() < 2 switch len(list) { case 0: - return nil + p.errorExpected(p.pos, "term") + return &Bad{p.pos, "term expected"} case 1: return list[0] } @@ -144,20 +146,16 @@ func (p *parser) parseExpression() Expression { var list Alternative for { - if x := p.parseSequence(); x != nil { - list = append(list, x) - } + list = append(list, p.parseSequence()) if p.tok != token.OR { break } p.next() } + // len(list) > 0 // no need for an Alternative node if list.Len() < 2 - switch len(list) { - case 0: - return nil - case 1: + if len(list) == 1 { return list[0] } @@ -168,7 +166,10 @@ func (p *parser) parseExpression() Expression { func (p *parser) parseProduction() *Production { name := p.parseIdentifier() p.expect(token.ASSIGN) - expr := p.parseExpression() + var expr Expression + if p.tok != token.PERIOD { + expr = p.parseExpression() + } p.expect(token.PERIOD) return &Production{name, expr} } diff --git a/src/pkg/exec/exec.go b/src/pkg/exec/exec.go index c6a5e06bb..935f24c21 100644 --- a/src/pkg/exec/exec.go +++ b/src/pkg/exec/exec.go @@ -62,9 +62,11 @@ type Cmd struct { Stdout io.Writer Stderr io.Writer + // Process is the underlying process, once started. + Process *os.Process + err os.Error // last error (from LookPath, stdin, stdout, stderr) - process *os.Process - finished bool // when Wait was called + finished bool // when Wait was called childFiles []*os.File closeAfterStart []io.Closer closeAfterWait []io.Closer @@ -205,7 +207,7 @@ func (c *Cmd) Start() os.Error { if c.err != nil { return c.err } - if c.process != nil { + if c.Process != nil { return os.NewError("exec: already started") } @@ -219,7 +221,7 @@ func (c *Cmd) Start() os.Error { } var err os.Error - c.process, err = os.StartProcess(c.Path, c.argv(), &os.ProcAttr{ + c.Process, err = os.StartProcess(c.Path, c.argv(), &os.ProcAttr{ Dir: c.Dir, Files: c.childFiles, Env: c.envv(), @@ -253,14 +255,14 @@ func (c *Cmd) Start() os.Error { // error is of type *os.Waitmsg. Other error types may be // returned for I/O problems. func (c *Cmd) Wait() os.Error { - if c.process == nil { + if c.Process == nil { return os.NewError("exec: not started") } if c.finished { return os.NewError("exec: Wait was already called") } c.finished = true - msg, err := c.process.Wait(0) + msg, err := c.Process.Wait(0) var copyError os.Error for _ = range c.goroutine { @@ -315,7 +317,7 @@ func (c *Cmd) StdinPipe() (io.WriteCloser, os.Error) { if c.Stdin != nil { return nil, os.NewError("exec: Stdin already set") } - if c.process != nil { + if c.Process != nil { return nil, os.NewError("exec: StdinPipe after process started") } pr, pw, err := os.Pipe() @@ -334,7 +336,7 @@ func (c *Cmd) StdoutPipe() (io.Reader, os.Error) { if c.Stdout != nil { return nil, os.NewError("exec: Stdout already set") } - if c.process != nil { + if c.Process != nil { return nil, os.NewError("exec: StdoutPipe after process started") } pr, pw, err := os.Pipe() @@ -353,7 +355,7 @@ func (c *Cmd) StderrPipe() (io.Reader, os.Error) { if c.Stderr != nil { return nil, os.NewError("exec: Stderr already set") } - if c.process != nil { + if c.Process != nil { return nil, os.NewError("exec: StderrPipe after process started") } pr, pw, err := os.Pipe() diff --git a/src/pkg/exp/gui/Makefile b/src/pkg/exp/gui/Makefile new file mode 100644 index 000000000..af065e4a5 --- /dev/null +++ b/src/pkg/exp/gui/Makefile @@ -0,0 +1,11 @@ +# Copyright 2009 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include ../../../Make.inc + +TARG=exp/gui +GOFILES=\ + gui.go\ + +include ../../../Make.pkg diff --git a/src/pkg/exp/draw/event.go b/src/pkg/exp/gui/gui.go index b777d912e..171499186 100644 --- a/src/pkg/exp/draw/event.go +++ b/src/pkg/exp/gui/gui.go @@ -2,17 +2,19 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package draw +// Package gui defines a basic graphical user interface programming model. +package gui import ( "image" + "image/draw" "os" ) // A Window represents a single graphics window. type Window interface { // Screen returns an editable Image for the window. - Screen() Image + Screen() draw.Image // FlushImage flushes changes made to Screen() back to screen. FlushImage() // EventChan returns a channel carrying UI events such as key presses, diff --git a/src/pkg/exp/draw/x11/Makefile b/src/pkg/exp/gui/x11/Makefile index 205b3a65b..88cc1e23b 100644 --- a/src/pkg/exp/draw/x11/Makefile +++ b/src/pkg/exp/gui/x11/Makefile @@ -4,7 +4,7 @@ include ../../../../Make.inc -TARG=exp/draw/x11 +TARG=exp/gui/x11 GOFILES=\ auth.go\ conn.go\ diff --git a/src/pkg/exp/draw/x11/auth.go b/src/pkg/exp/gui/x11/auth.go index d48936ac1..d48936ac1 100644 --- a/src/pkg/exp/draw/x11/auth.go +++ b/src/pkg/exp/gui/x11/auth.go diff --git a/src/pkg/exp/draw/x11/conn.go b/src/pkg/exp/gui/x11/conn.go index 23edc2c63..bc7ca63db 100644 --- a/src/pkg/exp/draw/x11/conn.go +++ b/src/pkg/exp/gui/x11/conn.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package x11 implements an X11 backend for the exp/draw package. +// Package x11 implements an X11 backend for the exp/gui package. // // The X protocol specification is at ftp://ftp.x.org/pub/X11R7.0/doc/PDF/proto.pdf. // A summary of the wire format can be found in XCB's xproto.xml. @@ -10,8 +10,9 @@ package x11 import ( "bufio" - "exp/draw" + "exp/gui" "image" + "image/draw" "io" "log" "net" @@ -43,7 +44,7 @@ type conn struct { img *image.RGBA eventc chan interface{} - mouseState draw.MouseEvent + mouseState gui.MouseEvent buf [256]byte // General purpose scratch buffer. @@ -53,7 +54,7 @@ type conn struct { } // writeSocket runs in its own goroutine, serving both FlushImage calls -// directly from the exp/draw client and indirectly from X expose events. +// directly from the exp/gui client and indirectly from X expose events. // It paints c.img to the X server via PutImage requests. func (c *conn) writeSocket() { defer c.c.Close() @@ -143,7 +144,7 @@ func (c *conn) Close() os.Error { func (c *conn) EventChan() <-chan interface{} { return c.eventc } -// readSocket runs in its own goroutine, reading X events and sending draw +// readSocket runs in its own goroutine, reading X events and sending gui // events on c's EventChan. func (c *conn) readSocket() { var ( @@ -155,7 +156,7 @@ func (c *conn) readSocket() { // X events are always 32 bytes long. if _, err := io.ReadFull(c.r, c.buf[0:32]); err != nil { if err != os.EOF { - c.eventc <- draw.ErrEvent{err} + c.eventc <- gui.ErrEvent{err} } return } @@ -165,7 +166,7 @@ func (c *conn) readSocket() { if cookie != 1 { // We issued only one request (GetKeyboardMapping) with a cookie of 1, // so we shouldn't get any other reply from the X server. - c.eventc <- draw.ErrEvent{os.NewError("x11: unexpected cookie")} + c.eventc <- gui.ErrEvent{os.NewError("x11: unexpected cookie")} return } keysymsPerKeycode = int(c.buf[1]) @@ -179,7 +180,7 @@ func (c *conn) readSocket() { u, err := readU32LE(c.r, c.buf[0:4]) if err != nil { if err != os.EOF { - c.eventc <- draw.ErrEvent{err} + c.eventc <- gui.ErrEvent{err} } return } @@ -204,11 +205,11 @@ func (c *conn) readSocket() { // TODO(nigeltao): Should we send KeyEvents for Shift/Ctrl/Alt? Should Shift-A send // the same int down the channel as the sent on just the A key? // TODO(nigeltao): How should IME events (e.g. key presses that should generate CJK text) work? Or - // is that outside the scope of the draw.Window interface? + // is that outside the scope of the gui.Window interface? if c.buf[0] == 0x03 { keysym = -keysym } - c.eventc <- draw.KeyEvent{keysym} + c.eventc <- gui.KeyEvent{keysym} case 0x04, 0x05: // Button press, button release. mask := 1 << (c.buf[1] - 1) if c.buf[0] == 0x04 { @@ -551,7 +552,7 @@ func (c *conn) handshake() os.Error { } // NewWindow calls NewWindowDisplay with $DISPLAY. -func NewWindow() (draw.Window, os.Error) { +func NewWindow() (gui.Window, os.Error) { display := os.Getenv("DISPLAY") if len(display) == 0 { return nil, os.NewError("$DISPLAY not set") @@ -559,10 +560,10 @@ func NewWindow() (draw.Window, os.Error) { return NewWindowDisplay(display) } -// NewWindowDisplay returns a new draw.Window, backed by a newly created and +// NewWindowDisplay returns a new gui.Window, backed by a newly created and // mapped X11 window. The X server to connect to is specified by the display // string, such as ":1". -func NewWindowDisplay(display string) (draw.Window, os.Error) { +func NewWindowDisplay(display string) (gui.Window, os.Error) { socket, displayStr, err := connect(display) if err != nil { return nil, err diff --git a/src/pkg/fmt/fmt_test.go b/src/pkg/fmt/fmt_test.go index caecb6fb8..122b9516b 100644 --- a/src/pkg/fmt/fmt_test.go +++ b/src/pkg/fmt/fmt_test.go @@ -132,15 +132,15 @@ var fmttests = []struct { {"%q", `"`, `"\""`}, {"%q", "\a\b\f\r\n\t\v", `"\a\b\f\r\n\t\v"`}, {"%q", "abc\xffdef", `"abc\xffdef"`}, - {"%q", "\u263a", `"\u263a"`}, + {"%q", "\u263a", `"☺"`}, {"%q", "\U0010ffff", `"\U0010ffff"`}, // escaped characters {"%q", 'x', `'x'`}, {"%q", 0, `'\x00'`}, {"%q", '\n', `'\n'`}, - {"%q", '\u1234', `'\u1234'`}, - {"%q", '\U00012345', `'\U00012345'`}, + {"%q", '\u0e00', `'\u0e00'`}, // not a printable rune. + {"%q", '\U000c2345', `'\U000c2345'`}, // not a printable rune. {"%q", int64(0x7FFFFFFF), `%!q(int64=2147483647)`}, {"%q", uint64(0xFFFFFFFF), `%!q(uint64=4294967295)`}, {"%q", '"', `'"'`}, @@ -148,7 +148,7 @@ var fmttests = []struct { // width {"%5s", "abc", " abc"}, - {"%2s", "\u263a", " \u263a"}, + {"%2s", "\u263a", " ☺"}, {"%-5s", "abc", "abc "}, {"%-8q", "abc", `"abc" `}, {"%05s", "abc", "00abc"}, @@ -158,9 +158,9 @@ var fmttests = []struct { {"%.5s", "日本語日本語", "日本語日本"}, {"%.5s", []byte("日本語日本語"), "日本語日本"}, {"%.5q", "abcdefghijklmnopqrstuvwxyz", `"abcde"`}, - {"%.3q", "日本語日本語", `"\u65e5\u672c\u8a9e"`}, - {"%.3q", []byte("日本語日本語"), `"\u65e5\u672c\u8a9e"`}, - {"%10.1q", "日本語日本語", ` "\u65e5"`}, + {"%.3q", "日本語日本語", `"日本語"`}, + {"%.3q", []byte("日本語日本語"), `"日本語"`}, + {"%10.1q", "日本語日本語", ` "æ—¥"`}, // integers {"%d", 12345, "12345"}, diff --git a/src/pkg/go/build/Makefile b/src/pkg/go/build/Makefile new file mode 100644 index 000000000..4411940ae --- /dev/null +++ b/src/pkg/go/build/Makefile @@ -0,0 +1,22 @@ +# Copyright 2009 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +include ../../../Make.inc + +TARG=go/build +GOFILES=\ + build.go\ + dir.go\ + path.go\ + syslist.go\ + +CLEANFILES+=syslist.go + +include ../../../Make.pkg + +syslist.go: ../../../Make.inc Makefile + echo '// Generated automatically by make.' >$@ + echo 'package build' >>$@ + echo 'const goosList = "$(GOOS_LIST)"' >>$@ + echo 'const goarchList = "$(GOARCH_LIST)"' >>$@ diff --git a/src/pkg/go/build/build.go b/src/pkg/go/build/build.go new file mode 100644 index 000000000..3cb8efe47 --- /dev/null +++ b/src/pkg/go/build/build.go @@ -0,0 +1,272 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package build provides tools for building Go packages. +package build + +import ( + "bytes" + "exec" + "fmt" + "os" + "path/filepath" + "runtime" + "strings" +) + +func (d *DirInfo) Build(targ string) ([]*Cmd, os.Error) { + b := &build{obj: "_obj/"} + + goarch := runtime.GOARCH + if g := os.Getenv("GOARCH"); g != "" { + goarch = g + } + var err os.Error + b.arch, err = ArchChar(goarch) + if err != nil { + return nil, err + } + + var gofiles = d.GoFiles // .go files to be built with gc + var ofiles []string // *.GOARCH files to be linked or packed + + // make build directory + b.mkdir(b.obj) + + // cgo + if len(d.CgoFiles) > 0 { + outGo, outObj := b.cgo(d.CgoFiles) + gofiles = append(gofiles, outGo...) + ofiles = append(ofiles, outObj...) + } + + // compile + if len(gofiles) > 0 { + ofile := b.obj + "_go_." + b.arch + b.gc(ofile, gofiles...) + ofiles = append(ofiles, ofile) + } + + // assemble + for _, sfile := range d.SFiles { + ofile := b.obj + sfile[:len(sfile)-1] + b.arch + b.asm(ofile, sfile) + ofiles = append(ofiles, ofile) + } + + if len(ofiles) == 0 { + return nil, os.NewError("make: no object files to build") + } + + if d.IsCommand() { + b.ld(targ, ofiles...) + } else { + b.gopack(targ, ofiles...) + } + + return b.cmds, nil +} + +type Cmd struct { + Args []string // command-line + Stdout string // write standard output to this file, "" is passthrough + Input []string // file paths (dependencies) + Output []string // file paths +} + +func (c *Cmd) String() string { + return strings.Join(c.Args, " ") +} + +func (c *Cmd) Run(dir string) os.Error { + out := new(bytes.Buffer) + cmd := exec.Command(c.Args[0], c.Args[1:]...) + cmd.Dir = dir + cmd.Stdout = out + cmd.Stderr = out + if c.Stdout != "" { + f, err := os.Create(filepath.Join(dir, c.Stdout)) + if err != nil { + return err + } + defer f.Close() + cmd.Stdout = f + } + if err := cmd.Run(); err != nil { + return fmt.Errorf("command %q: %v\n%v", c, err, out) + } + return nil +} + +func (c *Cmd) Clean(dir string) (err os.Error) { + for _, fn := range c.Output { + if e := os.RemoveAll(fn); err == nil { + err = e + } + } + return +} + +// ArchChar returns the architecture character for the given goarch. +// For example, ArchChar("amd64") returns "6". +func ArchChar(goarch string) (string, os.Error) { + switch goarch { + case "386": + return "8", nil + case "amd64": + return "6", nil + case "arm": + return "5", nil + } + return "", os.NewError("unsupported GOARCH " + goarch) +} + +type build struct { + cmds []*Cmd + obj string + arch string +} + +func (b *build) add(c Cmd) { + b.cmds = append(b.cmds, &c) +} + +func (b *build) mkdir(name string) { + b.add(Cmd{ + Args: []string{"mkdir", "-p", name}, + Output: []string{name}, + }) +} + +func (b *build) gc(ofile string, gofiles ...string) { + gc := b.arch + "g" + args := append([]string{gc, "-o", ofile}, gcImportArgs...) + args = append(args, gofiles...) + b.add(Cmd{ + Args: args, + Input: gofiles, + Output: []string{ofile}, + }) +} + +func (b *build) asm(ofile string, sfile string) { + asm := b.arch + "a" + b.add(Cmd{ + Args: []string{asm, "-o", ofile, sfile}, + Input: []string{sfile}, + Output: []string{ofile}, + }) +} + +func (b *build) ld(targ string, ofiles ...string) { + ld := b.arch + "l" + args := append([]string{ld, "-o", targ}, ldImportArgs...) + args = append(args, ofiles...) + b.add(Cmd{ + Args: args, + Input: ofiles, + Output: []string{targ}, + }) +} + +func (b *build) gopack(targ string, ofiles ...string) { + b.add(Cmd{ + Args: append([]string{"gopack", "grc", targ}, ofiles...), + Input: ofiles, + Output: []string{targ}, + }) +} + +func (b *build) cc(ofile string, cfiles ...string) { + cc := b.arch + "c" + dir := fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH) + inc := filepath.Join(runtime.GOROOT(), "pkg", dir) + args := []string{cc, "-FVw", "-I", inc, "-o", ofile} + b.add(Cmd{ + Args: append(args, cfiles...), + Input: cfiles, + Output: []string{ofile}, + }) +} + +func (b *build) gccCompile(ofile, cfile string) { + b.add(Cmd{ + Args: gccArgs(b.arch, "-o", ofile, "-c", cfile), + Input: []string{cfile}, + Output: []string{ofile}, + }) +} + +func (b *build) gccLink(ofile string, ofiles ...string) { + b.add(Cmd{ + Args: append(gccArgs(b.arch, "-o", ofile), ofiles...), + Input: ofiles, + Output: []string{ofile}, + }) +} + +func gccArgs(arch string, args ...string) []string { + // TODO(adg): HOST_CC + m := "-m32" + if arch == "6" { + m = "-m64" + } + return append([]string{"gcc", m, "-I", ".", "-g", "-fPIC", "-O2"}, args...) +} + +func (b *build) cgo(cgofiles []string) (outGo, outObj []string) { + // cgo + // TODO(adg): CGOPKGPATH + // TODO(adg): CGO_FLAGS + gofiles := []string{b.obj + "_cgo_gotypes.go"} + cfiles := []string{b.obj + "_cgo_main.c", b.obj + "_cgo_export.c"} + for _, fn := range cgofiles { + f := b.obj + fn[:len(fn)-2] + gofiles = append(gofiles, f+"cgo1.go") + cfiles = append(cfiles, f+"cgo2.c") + } + defunC := b.obj + "_cgo_defun.c" + output := append([]string{defunC}, gofiles...) + output = append(output, cfiles...) + b.add(Cmd{ + Args: append([]string{"cgo", "--"}, cgofiles...), + Input: cgofiles, + Output: output, + }) + outGo = append(outGo, gofiles...) + + // cc _cgo_defun.c + defunObj := b.obj + "_cgo_defun." + b.arch + b.cc(defunObj, defunC) + outObj = append(outObj, defunObj) + + // gcc + linkobj := make([]string, 0, len(cfiles)) + for _, cfile := range cfiles { + ofile := cfile[:len(cfile)-1] + "o" + b.gccCompile(ofile, cfile) + linkobj = append(linkobj, ofile) + if !strings.HasSuffix(ofile, "_cgo_main.o") { + outObj = append(outObj, ofile) + } + } + dynObj := b.obj + "_cgo1_.o" + b.gccLink(dynObj, linkobj...) + + // cgo -dynimport + importC := b.obj + "_cgo_import.c" + b.add(Cmd{ + Args: []string{"cgo", "-dynimport", dynObj}, + Stdout: importC, + Input: []string{dynObj}, + Output: []string{importC}, + }) + + // cc _cgo_import.ARCH + importObj := b.obj + "_cgo_import." + b.arch + b.cc(importObj, importC) + outObj = append(outObj, importObj) + + return +} diff --git a/src/pkg/go/build/build_test.go b/src/pkg/go/build/build_test.go new file mode 100644 index 000000000..c543eddbd --- /dev/null +++ b/src/pkg/go/build/build_test.go @@ -0,0 +1,57 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package build + +import ( + "os" + "path/filepath" + "runtime" + "strings" + "testing" +) + +var buildDirs = []string{ + "pkg/path", + "cmd/gofix", + "pkg/big", + "pkg/go/build/cgotest", +} + +func TestBuild(t *testing.T) { + out, err := filepath.Abs("_test/out") + if err != nil { + t.Fatal(err) + } + for _, d := range buildDirs { + if runtime.GOARCH == "arm" && strings.Contains(d, "/cgo") { + // no cgo for arm, yet. + continue + } + dir := filepath.Join(runtime.GOROOT(), "src", d) + testBuild(t, dir, out) + } +} + +func testBuild(t *testing.T, dir, targ string) { + d, err := ScanDir(dir, true) + if err != nil { + t.Error(err) + return + } + defer os.Remove(targ) + cmds, err := d.Build(targ) + if err != nil { + t.Error(err) + return + } + for _, c := range cmds { + t.Log("Run:", c) + err = c.Run(dir) + if err != nil { + t.Error(c, err) + return + } + } +} diff --git a/src/pkg/go/build/cgotest/file.go b/src/pkg/go/build/cgotest/file.go new file mode 100644 index 000000000..3b2a2e7d9 --- /dev/null +++ b/src/pkg/go/build/cgotest/file.go @@ -0,0 +1,45 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +A trivial example of wrapping a C library in Go. +For a more complex example and explanation, +see ../gmp/gmp.go. +*/ + +package stdio + +/* +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <errno.h> + +char* greeting = "hello, world"; +*/ +import "C" +import "unsafe" + +type File C.FILE + +// TODO(brainman): uncomment once stdout and stderr references are working on Windows. +//var Stdout = (*File)(C.stdout) +//var Stderr = (*File)(C.stderr) + +// Test reference to library symbol. +// Stdout and stderr are too special to be a reliable test. +var myerr = C.sys_errlist + +func (f *File) WriteString(s string) { + p := C.CString(s) + C.fputs(p, (*C.FILE)(f)) + C.free(unsafe.Pointer(p)) + f.Flush() +} + +func (f *File) Flush() { + C.fflush((*C.FILE)(f)) +} + +var Greeting = C.GoString(C.greeting) diff --git a/src/pkg/go/build/dir.go b/src/pkg/go/build/dir.go new file mode 100644 index 000000000..77e80bff0 --- /dev/null +++ b/src/pkg/go/build/dir.go @@ -0,0 +1,173 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package build + +import ( + "go/parser" + "go/token" + "log" + "os" + "path/filepath" + "strconv" + "strings" + "runtime" +) + +type DirInfo struct { + GoFiles []string // .go files in dir (excluding CgoFiles) + CgoFiles []string // .go files that import "C" + CFiles []string // .c files in dir + SFiles []string // .s files in dir + Imports []string // All packages imported by goFiles + PkgName string // Name of package in dir +} + +func (d *DirInfo) IsCommand() bool { + return d.PkgName == "main" +} + +// ScanDir returns a structure with details about the Go content found +// in the given directory. The file lists exclude: +// +// - files in package main (unless allowMain is true) +// - files in package documentation +// - files ending in _test.go +// - files starting with _ or . +// +// Only files that satisfy the goodOSArch function are included. +func ScanDir(dir string, allowMain bool) (info *DirInfo, err os.Error) { + f, err := os.Open(dir) + if err != nil { + return nil, err + } + dirs, err := f.Readdir(-1) + f.Close() + if err != nil { + return nil, err + } + + var di DirInfo + imported := make(map[string]bool) + pkgName := "" + fset := token.NewFileSet() + for i := range dirs { + d := &dirs[i] + if strings.HasPrefix(d.Name, "_") || + strings.HasPrefix(d.Name, ".") { + continue + } + if !goodOSArch(d.Name) { + continue + } + + switch filepath.Ext(d.Name) { + case ".go": + if strings.HasSuffix(d.Name, "_test.go") { + continue + } + case ".c": + di.CFiles = append(di.CFiles, d.Name) + continue + case ".s": + di.SFiles = append(di.SFiles, d.Name) + continue + default: + continue + } + + filename := filepath.Join(dir, d.Name) + pf, err := parser.ParseFile(fset, filename, nil, parser.ImportsOnly) + if err != nil { + return nil, err + } + s := string(pf.Name.Name) + if s == "main" && !allowMain { + continue + } + if s == "documentation" { + continue + } + if pkgName == "" { + pkgName = s + } else if pkgName != s { + // Only if all files in the directory are in package main + // do we return pkgName=="main". + // A mix of main and another package reverts + // to the original (allowMain=false) behaviour. + if s == "main" || pkgName == "main" { + return ScanDir(dir, false) + } + return nil, os.ErrorString("multiple package names in " + dir) + } + isCgo := false + for _, spec := range pf.Imports { + quoted := string(spec.Path.Value) + path, err := strconv.Unquote(quoted) + if err != nil { + log.Panicf("%s: parser returned invalid quoted string: <%s>", filename, quoted) + } + imported[path] = true + if path == "C" { + isCgo = true + } + } + if isCgo { + di.CgoFiles = append(di.CgoFiles, d.Name) + } else { + di.GoFiles = append(di.GoFiles, d.Name) + } + } + di.Imports = make([]string, len(imported)) + i := 0 + for p := range imported { + di.Imports[i] = p + i++ + } + return &di, nil +} + +// goodOSArch returns false if the filename contains a $GOOS or $GOARCH +// suffix which does not match the current system. +// The recognized filename formats are: +// +// name_$(GOOS).* +// name_$(GOARCH).* +// name_$(GOOS)_$(GOARCH).* +// +func goodOSArch(filename string) bool { + if dot := strings.Index(filename, "."); dot != -1 { + filename = filename[:dot] + } + l := strings.Split(filename, "_", -1) + n := len(l) + if n == 0 { + return true + } + if good, known := goodOS[l[n-1]]; known { + return good + } + if good, known := goodArch[l[n-1]]; known { + if !good || n < 2 { + return false + } + good, known = goodOS[l[n-2]] + return good || !known + } + return true +} + +var goodOS = make(map[string]bool) +var goodArch = make(map[string]bool) + +func init() { + goodOS = make(map[string]bool) + goodArch = make(map[string]bool) + for _, v := range strings.Fields(goosList) { + goodOS[v] = v == runtime.GOOS + } + for _, v := range strings.Fields(goarchList) { + goodArch[v] = v == runtime.GOARCH + } +} diff --git a/src/pkg/go/build/path.go b/src/pkg/go/build/path.go new file mode 100644 index 000000000..8ad39fb0f --- /dev/null +++ b/src/pkg/go/build/path.go @@ -0,0 +1,163 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package build + +import ( + "fmt" + "log" + "os" + "path/filepath" + "runtime" + "strings" +) + +// Path is a validated list of Trees derived from $GOPATH at init. +var Path []*Tree + +// Tree describes a Go source tree, either $GOROOT or one from $GOPATH. +type Tree struct { + Path string + Goroot bool +} + +func newTree(p string) (*Tree, os.Error) { + if !filepath.IsAbs(p) { + return nil, os.NewError("must be absolute") + } + ep, err := filepath.EvalSymlinks(p) + if err != nil { + return nil, err + } + return &Tree{Path: ep}, nil +} + +// SrcDir returns the tree's package source directory. +func (t *Tree) SrcDir() string { + if t.Goroot { + return filepath.Join(t.Path, "src", "pkg") + } + return filepath.Join(t.Path, "src") +} + +// PkgDir returns the tree's package object directory. +func (t *Tree) PkgDir() string { + goos, goarch := runtime.GOOS, runtime.GOARCH + if e := os.Getenv("GOOS"); e != "" { + goos = e + } + if e := os.Getenv("GOARCH"); e != "" { + goarch = e + } + return filepath.Join(t.Path, "pkg", goos+"_"+goarch) +} + +// BinDir returns the tree's binary executable directory. +func (t *Tree) BinDir() string { + return filepath.Join(t.Path, "bin") +} + +// HasSrc returns whether the given package's +// source can be found inside this Tree. +func (t *Tree) HasSrc(pkg string) bool { + fi, err := os.Stat(filepath.Join(t.SrcDir(), pkg)) + if err != nil { + return false + } + return fi.IsDirectory() +} + +// HasPkg returns whether the given package's +// object file can be found inside this Tree. +func (t *Tree) HasPkg(pkg string) bool { + fi, err := os.Stat(filepath.Join(t.PkgDir(), pkg+".a")) + if err != nil { + return false + } + return fi.IsRegular() + // TODO(adg): check object version is consistent +} + +var ErrNotFound = os.NewError("package could not be found locally") + +// FindTree takes an import or filesystem path and returns the +// tree where the package source should be and the package import path. +func FindTree(path string) (tree *Tree, pkg string, err os.Error) { + if isLocalPath(path) { + if path, err = filepath.Abs(path); err != nil { + return + } + for _, t := range Path { + tpath := t.SrcDir() + string(filepath.Separator) + if !strings.HasPrefix(path, tpath) { + continue + } + tree = t + pkg = path[len(tpath):] + return + } + err = fmt.Errorf("path %q not inside a GOPATH", path) + return + } + tree = defaultTree + pkg = path + for _, t := range Path { + if t.HasSrc(pkg) { + tree = t + return + } + } + err = ErrNotFound + return +} + +// isLocalPath returns whether the given path is local (/foo ./foo ../foo . ..) +func isLocalPath(s string) bool { + const sep = string(filepath.Separator) + return strings.HasPrefix(s, sep) || strings.HasPrefix(s, "."+sep) || strings.HasPrefix(s, ".."+sep) || s == "." || s == ".." +} + +var ( + // argument lists used by the build's gc and ld methods + gcImportArgs []string + ldImportArgs []string + + // default tree for remote packages + defaultTree *Tree +) + +// set up Path: parse and validate GOROOT and GOPATH variables +func init() { + root := runtime.GOROOT() + p, err := newTree(root) + if err != nil { + log.Fatalf("Invalid GOROOT %q: %v", root, err) + } + p.Goroot = true + Path = []*Tree{p} + + for _, p := range filepath.SplitList(os.Getenv("GOPATH")) { + if p == "" { + continue + } + t, err := newTree(p) + if err != nil { + log.Printf("Invalid GOPATH %q: %v", p, err) + continue + } + Path = append(Path, t) + gcImportArgs = append(gcImportArgs, "-I", t.PkgDir()) + ldImportArgs = append(ldImportArgs, "-L", t.PkgDir()) + + // select first GOPATH entry as default + if defaultTree == nil { + defaultTree = t + } + } + + // use GOROOT if no valid GOPATH specified + if defaultTree == nil { + defaultTree = Path[0] + } +} diff --git a/src/pkg/go/build/syslist_test.go b/src/pkg/go/build/syslist_test.go new file mode 100644 index 000000000..eb0e5dcb6 --- /dev/null +++ b/src/pkg/go/build/syslist_test.go @@ -0,0 +1,62 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package build + +import ( + "runtime" + "testing" +) + +var ( + thisOS = runtime.GOOS + thisArch = runtime.GOARCH + otherOS = anotherOS() + otherArch = anotherArch() +) + +func anotherOS() string { + if thisOS != "darwin" { + return "darwin" + } + return "linux" +} + +func anotherArch() string { + if thisArch != "amd64" { + return "amd64" + } + return "386" +} + +type GoodFileTest struct { + name string + result bool +} + +var tests = []GoodFileTest{ + {"file.go", true}, + {"file.c", true}, + {"file_foo.go", true}, + {"file_" + thisArch + ".go", true}, + {"file_" + otherArch + ".go", false}, + {"file_" + thisOS + ".go", true}, + {"file_" + otherOS + ".go", false}, + {"file_" + thisOS + "_" + thisArch + ".go", true}, + {"file_" + otherOS + "_" + thisArch + ".go", false}, + {"file_" + thisOS + "_" + otherArch + ".go", false}, + {"file_" + otherOS + "_" + otherArch + ".go", false}, + {"file_foo_" + thisArch + ".go", true}, + {"file_foo_" + otherArch + ".go", false}, + {"file_" + thisOS + ".c", true}, + {"file_" + otherOS + ".c", false}, +} + +func TestGoodOSArch(t *testing.T) { + for _, test := range tests { + if goodOSArch(test.name) != test.result { + t.Fatalf("goodOSArch(%q) != %v", test.name, test.result) + } + } +} diff --git a/src/pkg/go/scanner/scanner_test.go b/src/pkg/go/scanner/scanner_test.go index 8af972838..ee1e830a1 100644 --- a/src/pkg/go/scanner/scanner_test.go +++ b/src/pkg/go/scanner/scanner_test.go @@ -652,7 +652,7 @@ var errors = []struct { }{ {"\a", token.ILLEGAL, 0, "illegal character '\\a'"}, {`#`, token.ILLEGAL, 0, "illegal character '#'"}, - {`…`, token.ILLEGAL, 0, "illegal character '\\u2026'"}, + {`…`, token.ILLEGAL, 0, "illegal character '…'"}, {`' '`, token.CHAR, 0, ""}, {`''`, token.CHAR, 0, "illegal character literal"}, {`'\8'`, token.CHAR, 2, "unknown escape sequence"}, diff --git a/src/pkg/go/types/gcimporter.go b/src/pkg/go/types/gcimporter.go index 377c45ad6..2cfed7726 100644 --- a/src/pkg/go/types/gcimporter.go +++ b/src/pkg/go/types/gcimporter.go @@ -403,7 +403,7 @@ func (p *gcParser) parseStructType() Type { } -// Parameter = ( identifier | "?" ) [ "..." ] Type . +// Parameter = ( identifier | "?" ) [ "..." ] Type [ ":" string_lit ] . // func (p *gcParser) parseParameter() (par *ast.Object, isVariadic bool) { name := p.parseName() @@ -415,6 +415,11 @@ func (p *gcParser) parseParameter() (par *ast.Object, isVariadic bool) { isVariadic = true } ptyp := p.parseType() + // ignore argument tag + if p.tok == ':' { + p.next() + p.expect(scanner.String) + } par = ast.NewObj(ast.Var, name) par.Type = ptyp return diff --git a/src/pkg/html/doc.go b/src/pkg/html/doc.go index 55135c3d0..5bc063086 100644 --- a/src/pkg/html/doc.go +++ b/src/pkg/html/doc.go @@ -4,6 +4,7 @@ /* Package html implements an HTML5-compliant tokenizer and parser. +INCOMPLETE. Tokenization is done by creating a Tokenizer for an io.Reader r. It is the caller's responsibility to ensure that r provides UTF-8 encoded HTML. diff --git a/src/pkg/html/token.go b/src/pkg/html/token.go index 6d8eb604e..23c95ece6 100644 --- a/src/pkg/html/token.go +++ b/src/pkg/html/token.go @@ -355,6 +355,33 @@ loop: return z.buf[i0:i], z.trim(i) } +// attrName finds the largest attribute name at the start +// of z.buf[i:] and returns it lower-cased, as well +// as the trimmed cursor location after that word. +// +// http://dev.w3.org/html5/spec/Overview.html#syntax-attribute-name +// TODO: unicode characters +func (z *Tokenizer) attrName(i int) ([]byte, int) { + i0 := i +loop: + for ; i < z.p1; i++ { + c := z.buf[i] + switch c { + case '<', '>', '"', '\'', '/', '=': + break loop + } + switch { + case 'A' <= c && c <= 'Z': + z.buf[i] = c + 'a' - 'A' + case c > ' ' && c < 0x7f: + // No-op. + default: + break loop + } + } + return z.buf[i0:i], z.trim(i) +} + // Text returns the unescaped text of a TextToken or a CommentToken. // The contents of the returned slice may change on the next call to Next. func (z *Tokenizer) Text() []byte { @@ -399,7 +426,7 @@ func (z *Tokenizer) TagName() (name []byte, hasAttr bool) { // attribute for the current tag token and whether there are more attributes. // The contents of the returned slices may change on the next call to Next. func (z *Tokenizer) TagAttr() (key, val []byte, moreAttr bool) { - key, i := z.word(z.p0, true) + key, i := z.attrName(z.p0) // Check for an empty attribute value. if i == z.p1 { z.p0 = i diff --git a/src/pkg/html/token_test.go b/src/pkg/html/token_test.go index 6291af600..c17b436aa 100644 --- a/src/pkg/html/token_test.go +++ b/src/pkg/html/token_test.go @@ -126,6 +126,11 @@ var tokenTests = []tokenTest{ `<input value="yes" foo="BAR">`, }, { + "Unquoted attribute value, spaces", + `<input value = yes FOO = BAR>`, + `<input value="yes" foo="BAR">`, + }, + { "Unquoted attribute value, trailing space", `<input value=yes FOO=BAR >`, `<input value="yes" foo="BAR">`, @@ -145,6 +150,11 @@ var tokenTests = []tokenTest{ `<input value="I'm an attribute" FOO="BAR">`, `<input value="I'm an attribute" foo="BAR">`, }, + { + "Attribute name characters", + `<meta http-equiv="content-type">`, + `<meta http-equiv="content-type">`, + }, } func TestTokenizer(t *testing.T) { diff --git a/src/pkg/http/client.go b/src/pkg/http/client.go index 7e1d65df3..71b037042 100644 --- a/src/pkg/http/client.go +++ b/src/pkg/http/client.go @@ -7,7 +7,6 @@ package http import ( - "bytes" "encoding/base64" "fmt" "io" @@ -240,7 +239,7 @@ func (c *Client) Post(url string, bodyType string, body io.Reader) (r *Response, // Caller should close r.Body when done reading from it. // // PostForm is a wrapper around DefaultClient.PostForm -func PostForm(url string, data map[string]string) (r *Response, err os.Error) { +func PostForm(url string, data Values) (r *Response, err os.Error) { return DefaultClient.PostForm(url, data) } @@ -248,17 +247,8 @@ func PostForm(url string, data map[string]string) (r *Response, err os.Error) { // with data's keys and values urlencoded as the request body. // // Caller should close r.Body when done reading from it. -func (c *Client) PostForm(url string, data map[string]string) (r *Response, err os.Error) { - return c.Post(url, "application/x-www-form-urlencoded", urlencode(data)) -} - -// TODO: remove this function when PostForm takes a multimap. -func urlencode(data map[string]string) (b *bytes.Buffer) { - m := make(map[string][]string, len(data)) - for k, v := range data { - m[k] = []string{v} - } - return bytes.NewBuffer([]byte(EncodeQuery(m))) +func (c *Client) PostForm(url string, data Values) (r *Response, err os.Error) { + return c.Post(url, "application/x-www-form-urlencoded", strings.NewReader(data.Encode())) } // Head issues a HEAD to the specified URL. If the response is one of the diff --git a/src/pkg/http/client_test.go b/src/pkg/http/client_test.go index 822a8889c..9ef81d9d4 100644 --- a/src/pkg/http/client_test.go +++ b/src/pkg/http/client_test.go @@ -109,7 +109,10 @@ func TestPostFormRequestFormat(t *testing.T) { client := &Client{Transport: tr} url := "http://dummy.faketld/" - form := map[string]string{"foo": "bar"} + form := make(Values) + form.Set("foo", "bar") + form.Add("foo", "bar2") + form.Set("bar", "baz") client.PostForm(url, form) // Note: doesn't hit network if tr.req.Method != "POST" { @@ -127,10 +130,17 @@ func TestPostFormRequestFormat(t *testing.T) { if tr.req.Close { t.Error("got Close true, want false") } - if g, e := tr.req.ContentLength, int64(len("foo=bar")); g != e { + expectedBody := "foo=bar&foo=bar2&bar=baz" + if g, e := tr.req.ContentLength, int64(len(expectedBody)); g != e { t.Errorf("got ContentLength %d, want %d", g, e) } - + bodyb, err := ioutil.ReadAll(tr.req.Body) + if err != nil { + t.Fatalf("ReadAll on req.Body: %v", err) + } + if g := string(bodyb); g != expectedBody { + t.Errorf("got body %q, want %q", g, expectedBody) + } } func TestRedirects(t *testing.T) { diff --git a/src/pkg/http/httptest/server.go b/src/pkg/http/httptest/server.go index 8e385d045..879f04f33 100644 --- a/src/pkg/http/httptest/server.go +++ b/src/pkg/http/httptest/server.go @@ -108,29 +108,24 @@ func (s *Server) CloseClientConnections() { // "127.0.0.1" and "[::1]", expiring at the last second of 2049 (the end // of ASN.1 time). var localhostCert = []byte(`-----BEGIN CERTIFICATE----- -MIIBwTCCASugAwIBAgIBADALBgkqhkiG9w0BAQUwADAeFw0xMTAzMzEyMDI1MDda -Fw00OTEyMzEyMzU5NTlaMAAwggCdMAsGCSqGSIb3DQEBAQOCAIwAMIIAhwKCAIB6 -oy4iT42G6qk+GGn5VL5JlnJT6ZG5cqaMNFaNGlIxNb6CPUZLKq2sM3gRaimsktIw -nNAcNwQGHpe1tZo+J/Pl04JTt71Y/TTAxy7OX27aZf1Rpt0SjdZ7vTPnFDPNsHGe -KBKvPt55l2+YKjkZmV7eRevsVbpkNvNGB+T5d4Ge/wIBA6NPME0wDgYDVR0PAQH/ -BAQDAgCgMA0GA1UdDgQGBAQBAgMEMA8GA1UdIwQIMAaABAECAwQwGwYDVR0RBBQw -EoIJMTI3LjAuMC4xggVbOjoxXTALBgkqhkiG9w0BAQUDggCBAHC3gbdvc44vs+wD -g2kONiENnx8WKc0UTGg/TOXS3gaRb+CUIQtHWja65l8rAfclEovjHgZ7gx8brO0W -JuC6p3MUAKsgOssIrrRIx2rpnfcmFVMzguCmrMNVmKUAalw18Yp0F72xYAIitVQl -kJrLdIhBajcJRYu/YGltHQRaXuVt +MIIBOTCB5qADAgECAgEAMAsGCSqGSIb3DQEBBTAAMB4XDTcwMDEwMTAwMDAwMFoX +DTQ5MTIzMTIzNTk1OVowADBaMAsGCSqGSIb3DQEBAQNLADBIAkEAsuA5mAFMj6Q7 +qoBzcvKzIq4kzuT5epSp2AkcQfyBHm7K13Ws7u+0b5Vb9gqTf5cAiIKcrtrXVqkL +8i1UQF6AzwIDAQABo08wTTAOBgNVHQ8BAf8EBAMCACQwDQYDVR0OBAYEBAECAwQw +DwYDVR0jBAgwBoAEAQIDBDAbBgNVHREEFDASggkxMjcuMC4wLjGCBVs6OjFdMAsG +CSqGSIb3DQEBBQNBAJH30zjLWRztrWpOCgJL8RQWLaKzhK79pVhAx6q/3NrF16C7 ++l1BRZstTwIGdoGId8BRpErK1TXkniFb95ZMynM= -----END CERTIFICATE----- `) // localhostKey is the private key for localhostCert. var localhostKey = []byte(`-----BEGIN RSA PRIVATE KEY----- -MIIBkgIBAQKCAIB6oy4iT42G6qk+GGn5VL5JlnJT6ZG5cqaMNFaNGlIxNb6CPUZL -Kq2sM3gRaimsktIwnNAcNwQGHpe1tZo+J/Pl04JTt71Y/TTAxy7OX27aZf1Rpt0S -jdZ7vTPnFDPNsHGeKBKvPt55l2+YKjkZmV7eRevsVbpkNvNGB+T5d4Ge/wIBAwKC -AIBRwh7Bil5Z8cYpZZv7jdQxDvbim7Z7ocRdeDmzZuF2I9RW04QyHHPIIlALnBvI -YeF1veASz1gEFGUjzmbUGqKYSbCoTzXoev+F4bmbRxcX9sOmtslqvhMSHRSzA5NH -aDVI3Hn4wvBVD8gePu8ACWqvPGbCiql11OKCMfjlPn2uuwJAx/24/F5DjXZ6hQQ7 -HxScOxKrpx5WnA9r1wZTltOTZkhRRzuLc21WJeE3M15QUdWi3zZxCKRFoth65HEs -jy9YHQJAnPueRI44tz79b5QqVbeaOMUr7ZCb1Kp0uo6G+ANPLdlfliAupwij2eIz -mHRJOWk0jBtXfRft1McH2H51CpXAyw== +MIIBPQIBAAJBALLgOZgBTI+kO6qAc3LysyKuJM7k+XqUqdgJHEH8gR5uytd1rO7v +tG+VW/YKk3+XAIiCnK7a11apC/ItVEBegM8CAwEAAQJBAI5sxq7naeR9ahyqRkJi +SIv2iMxLuPEHaezf5CYOPWjSjBPyVhyRevkhtqEjF/WkgL7C2nWpYHsUcBDBQVF0 +3KECIQDtEGB2ulnkZAahl3WuJziXGLB+p8Wgx7wzSM6bHu1c6QIhAMEp++CaS+SJ +/TrU0zwY/fW4SvQeb49BPZUF3oqR8Xz3AiEA1rAJHBzBgdOQKdE3ksMUPcnvNJSN +poCcELmz2clVXtkCIQCLytuLV38XHToTipR4yMl6O+6arzAjZ56uq7m7ZRV0TwIh +AM65XAOw8Dsg9Kq78aYXiOEDc5DL0sbFUu/SlmRcCg93 -----END RSA PRIVATE KEY----- `) diff --git a/src/pkg/http/readrequest_test.go b/src/pkg/http/readrequest_test.go index 19e2ff774..d93e573f5 100644 --- a/src/pkg/http/readrequest_test.go +++ b/src/pkg/http/readrequest_test.go @@ -64,7 +64,7 @@ var reqTests = []reqTest{ Host: "www.techcrunch.com", Referer: "", UserAgent: "Fake", - Form: map[string][]string{}, + Form: Values{}, }, "abcdef\n", @@ -99,7 +99,7 @@ var reqTests = []reqTest{ Host: "test", Referer: "", UserAgent: "", - Form: map[string][]string{}, + Form: Values{}, }, "", diff --git a/src/pkg/http/request.go b/src/pkg/http/request.go index 2f6b651c3..bdc3a7e4f 100644 --- a/src/pkg/http/request.go +++ b/src/pkg/http/request.go @@ -90,10 +90,10 @@ type Request struct { // // then // - // Header = map[string]string{ - // "Accept-Encoding": "gzip, deflate", - // "Accept-Language": "en-us", - // "Connection": "keep-alive", + // Header = map[string][]string{ + // "Accept-Encoding": {"gzip, deflate"}, + // "Accept-Language": {"en-us"}, + // "Connection": {"keep-alive"}, // } // // HTTP defines that header names are case-insensitive. @@ -141,7 +141,7 @@ type Request struct { UserAgent string // The parsed form. Only available after ParseForm is called. - Form map[string][]string + Form Values // The parsed multipart form, including file uploads. // Only available after ParseMultipartForm is called. @@ -238,9 +238,9 @@ const defaultUserAgent = "Go http package" // TransferEncoding // Body // -// If Body is present but Content-Length is <= 0, Write adds -// "Transfer-Encoding: chunked" to the header. Body is closed after -// it is sent. +// If Body is present, Content-Length is <= 0 and TransferEncoding +// hasn't been set to "identity", Write adds "Transfer-Encoding: +// chunked" to the header. Body is closed after it is sent. func (req *Request) Write(w io.Writer) os.Error { return req.write(w, false) } @@ -488,6 +488,11 @@ func NewRequest(method, url string, body io.Reader) (*Request, os.Error) { default: req.ContentLength = -1 // chunked } + if req.ContentLength == 0 { + // To prevent chunking and disambiguate this + // from the default ContentLength zero value. + req.TransferEncoding = []string{"identity"} + } } return req, nil @@ -597,18 +602,56 @@ func ReadRequest(b *bufio.Reader) (req *Request, err os.Error) { return req, nil } +// Values maps a string key to a list of values. +// It is typically used for query parameters and form values. +// Unlike in the Header map, the keys in a Values map +// are case-sensitive. +type Values map[string][]string + +// Get gets the first value associated with the given key. +// If there are no values associated with the key, Get returns +// the empty string. To access multiple values, use the map +// directly. +func (v Values) Get(key string) string { + if v == nil { + return "" + } + vs, ok := v[key] + if !ok || len(vs) == 0 { + return "" + } + return vs[0] +} + +// Set sets the key to value. It replaces any existing +// values. +func (v Values) Set(key, value string) { + v[key] = []string{value} +} + +// Add adds the key to value. It appends to any existing +// values associated with key. +func (v Values) Add(key, value string) { + v[key] = append(v[key], value) +} + +// Del deletes the values associated with key. +func (v Values) Del(key string) { + v[key] = nil, false +} + // ParseQuery parses the URL-encoded query string and returns // a map listing the values specified for each key. // ParseQuery always returns a non-nil map containing all the // valid query parameters found; err describes the first decoding error // encountered, if any. -func ParseQuery(query string) (m map[string][]string, err os.Error) { - m = make(map[string][]string) +func ParseQuery(query string) (m Values, err os.Error) { + m = make(Values) err = parseQuery(m, query) return } -func parseQuery(m map[string][]string, query string) (err os.Error) { +func parseQuery(m Values, query string) (err os.Error) { for _, kv := range strings.Split(query, "&", -1) { if len(kv) == 0 { continue @@ -641,7 +684,7 @@ func (r *Request) ParseForm() (err os.Error) { return } - r.Form = make(map[string][]string) + r.Form = make(Values) if r.URL != nil { err = parseQuery(r.Form, r.URL.RawQuery) } diff --git a/src/pkg/http/requestwrite_test.go b/src/pkg/http/requestwrite_test.go index 2889048a9..98fbcf459 100644 --- a/src/pkg/http/requestwrite_test.go +++ b/src/pkg/http/requestwrite_test.go @@ -274,13 +274,45 @@ func (rc *closeChecker) Close() os.Error { // TestRequestWriteClosesBody tests that Request.Write does close its request.Body. // It also indirectly tests NewRequest and that it doesn't wrap an existing Closer -// inside a NopCloser. +// inside a NopCloser, and that it serializes it correctly. func TestRequestWriteClosesBody(t *testing.T) { rc := &closeChecker{Reader: strings.NewReader("my body")} - req, _ := NewRequest("GET", "http://foo.com/", rc) + req, _ := NewRequest("POST", "http://foo.com/", rc) + if g, e := req.ContentLength, int64(-1); g != e { + t.Errorf("got req.ContentLength %d, want %d", g, e) + } buf := new(bytes.Buffer) req.Write(buf) if !rc.closed { t.Error("body not closed after write") } + if g, e := buf.String(), "POST / HTTP/1.1\r\nHost: foo.com\r\nUser-Agent: Go http package\r\nTransfer-Encoding: chunked\r\n\r\n7\r\nmy body\r\n0\r\n\r\n"; g != e { + t.Errorf("write:\n got: %s\nwant: %s", g, e) + } +} + +func TestZeroLengthNewRequest(t *testing.T) { + var buf bytes.Buffer + + // Writing with default identity encoding + req, _ := NewRequest("PUT", "http://foo.com/", strings.NewReader("")) + if len(req.TransferEncoding) == 0 || req.TransferEncoding[0] != "identity" { + t.Fatalf("got req.TransferEncoding of %v, want %v", req.TransferEncoding, []string{"identity"}) + } + if g, e := req.ContentLength, int64(0); g != e { + t.Errorf("got req.ContentLength %d, want %d", g, e) + } + req.Write(&buf) + if g, e := buf.String(), "PUT / HTTP/1.1\r\nHost: foo.com\r\nUser-Agent: Go http package\r\nContent-Length: 0\r\n\r\n"; g != e { + t.Errorf("identity write:\n got: %s\nwant: %s", g, e) + } + + // Overriding identity encoding and forcing chunked. + req, _ = NewRequest("PUT", "http://foo.com/", strings.NewReader("")) + req.TransferEncoding = nil + buf.Reset() + req.Write(&buf) + if g, e := buf.String(), "PUT / HTTP/1.1\r\nHost: foo.com\r\nUser-Agent: Go http package\r\nTransfer-Encoding: chunked\r\n\r\n0\r\n\r\n"; g != e { + t.Errorf("chunked write:\n got: %s\nwant: %s", g, e) + } } diff --git a/src/pkg/http/serve_test.go b/src/pkg/http/serve_test.go index c923c8a76..dc4594a79 100644 --- a/src/pkg/http/serve_test.go +++ b/src/pkg/http/serve_test.go @@ -12,12 +12,14 @@ import ( "fmt" . "http" "http/httptest" + "io" "io/ioutil" "log" "os" "net" "reflect" "strings" + "syscall" "testing" "time" ) @@ -494,6 +496,12 @@ func TestHeadResponses(t *testing.T) { if err != ErrBodyNotAllowed { t.Errorf("on Write, expected ErrBodyNotAllowed, got %v", err) } + + // Also exercise the ReaderFrom path + _, err = io.Copy(w, strings.NewReader("Ignored body")) + if err != ErrBodyNotAllowed { + t.Errorf("on Copy, expected ErrBodyNotAllowed, got %v", err) + } })) defer ts.Close() res, err := Head(ts.URL) @@ -773,6 +781,42 @@ func TestHandlerPanic(t *testing.T) { } } +type errorListener struct { + errs []os.Error +} + +func (l *errorListener) Accept() (c net.Conn, err os.Error) { + if len(l.errs) == 0 { + return nil, os.EOF + } + err = l.errs[0] + l.errs = l.errs[1:] + return +} + +func (l *errorListener) Close() os.Error { + return nil +} + +func (l *errorListener) Addr() net.Addr { + return dummyAddr("test-address") +} + +func TestAcceptMaxFds(t *testing.T) { + log.SetOutput(ioutil.Discard) // is noisy otherwise + defer log.SetOutput(os.Stderr) + + ln := &errorListener{[]os.Error{ + &net.OpError{ + Op: "accept", + Error: os.Errno(syscall.EMFILE), + }}} + err := Serve(ln, HandlerFunc(HandlerFunc(func(ResponseWriter, *Request) {}))) + if err != os.EOF { + t.Errorf("got error %v, want EOF", err) + } +} + func BenchmarkClientServer(b *testing.B) { b.StopTimer() ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, r *Request) { diff --git a/src/pkg/http/server.go b/src/pkg/http/server.go index 93d9d2ff4..d4638f127 100644 --- a/src/pkg/http/server.go +++ b/src/pkg/http/server.go @@ -129,7 +129,7 @@ func (r *response) ReadFrom(src io.Reader) (n int64, err os.Error) { // WriteHeader if it hasn't been called yet, and WriteHeader // is what sets r.chunking. r.Flush() - if !r.chunking { + if !r.chunking && r.bodyAllowed() { if rf, ok := r.conn.rwc.(io.ReaderFrom); ok { n, err = rf.ReadFrom(src) r.written += n @@ -335,6 +335,15 @@ func (w *response) WriteHeader(code int) { io.WriteString(w.conn.buf, "\r\n") } +// bodyAllowed returns true if a Write is allowed for this response type. +// It's illegal to call this before the header has been flushed. +func (w *response) bodyAllowed() bool { + if !w.wroteHeader { + panic("") + } + return w.status != StatusNotModified && w.req.Method != "HEAD" +} + func (w *response) Write(data []byte) (n int, err os.Error) { if w.conn.hijacked { log.Print("http: response.Write on hijacked connection") @@ -346,9 +355,7 @@ func (w *response) Write(data []byte) (n int, err os.Error) { if len(data) == 0 { return 0, nil } - - if w.status == StatusNotModified || w.req.Method == "HEAD" { - // Must not have body. + if !w.bodyAllowed() { return 0, ErrBodyNotAllowed } @@ -860,6 +867,10 @@ func (srv *Server) Serve(l net.Listener) os.Error { for { rw, e := l.Accept() if e != nil { + if ne, ok := e.(net.Error); ok && ne.Temporary() { + log.Printf("http: Accept error: %v", e) + continue + } return e } if srv.ReadTimeout != 0 { diff --git a/src/pkg/http/transfer.go b/src/pkg/http/transfer.go index 062e7a0ff..b54508e7a 100644 --- a/src/pkg/http/transfer.go +++ b/src/pkg/http/transfer.go @@ -38,6 +38,9 @@ func newTransferWriter(r interface{}) (t *transferWriter, err os.Error) { t.TransferEncoding = rr.TransferEncoding t.Trailer = rr.Trailer atLeastHTTP11 = rr.ProtoAtLeast(1, 1) + if t.Body != nil && t.ContentLength <= 0 && len(t.TransferEncoding) == 0 && atLeastHTTP11 { + t.TransferEncoding = []string{"chunked"} + } case *Response: t.Body = rr.Body t.ContentLength = rr.ContentLength @@ -95,7 +98,7 @@ func (t *transferWriter) WriteHeader(w io.Writer) (err os.Error) { if err != nil { return } - } else if t.ContentLength > 0 || t.ResponseToHEAD { + } else if t.ContentLength > 0 || t.ResponseToHEAD || (t.ContentLength == 0 && isIdentity(t.TransferEncoding)) { io.WriteString(w, "Content-Length: ") _, err = io.WriteString(w, strconv.Itoa64(t.ContentLength)+"\r\n") if err != nil { @@ -289,6 +292,9 @@ func readTransfer(msg interface{}, r *bufio.Reader) (err os.Error) { // Checks whether chunked is part of the encodings stack func chunked(te []string) bool { return len(te) > 0 && te[0] == "chunked" } +// Checks whether the encoding is explicitly "identity". +func isIdentity(te []string) bool { return len(te) == 1 && te[0] == "identity" } + // Sanitize transfer encoding func fixTransferEncoding(requestMethod string, header Header) ([]string, os.Error) { raw, present := header["Transfer-Encoding"] diff --git a/src/pkg/http/url.go b/src/pkg/http/url.go index d7ee14ee8..05b1662d3 100644 --- a/src/pkg/http/url.go +++ b/src/pkg/http/url.go @@ -486,10 +486,14 @@ func (url *URL) String() string { return result } -// EncodeQuery encodes the query represented as a multimap. -func EncodeQuery(m map[string][]string) string { - parts := make([]string, 0, len(m)) // will be large enough for most uses - for k, vs := range m { +// Encode encodes the values into ``URL encoded'' form. +// e.g. "foo=bar&bar=baz" +func (v Values) Encode() string { + if v == nil { + return "" + } + parts := make([]string, 0, len(v)) // will be large enough for most uses + for k, vs := range v { prefix := URLEscape(k) + "=" for _, v := range vs { parts = append(parts, prefix+URLEscape(v)) @@ -593,3 +597,9 @@ func (base *URL) ResolveReference(ref *URL) *URL { url.Raw = url.String() return url } + +// Query parses RawQuery and returns the corresponding values. +func (u *URL) Query() Values { + v, _ := ParseQuery(u.RawQuery) + return v +} diff --git a/src/pkg/http/url_test.go b/src/pkg/http/url_test.go index d8863f3d3..eaec5872a 100644 --- a/src/pkg/http/url_test.go +++ b/src/pkg/http/url_test.go @@ -538,23 +538,21 @@ func TestUnescapeUserinfo(t *testing.T) { } } -type qMap map[string][]string - type EncodeQueryTest struct { - m qMap + m Values expected string expected1 string } var encodeQueryTests = []EncodeQueryTest{ {nil, "", ""}, - {qMap{"q": {"puppies"}, "oe": {"utf8"}}, "q=puppies&oe=utf8", "oe=utf8&q=puppies"}, - {qMap{"q": {"dogs", "&", "7"}}, "q=dogs&q=%26&q=7", "q=dogs&q=%26&q=7"}, + {Values{"q": {"puppies"}, "oe": {"utf8"}}, "q=puppies&oe=utf8", "oe=utf8&q=puppies"}, + {Values{"q": {"dogs", "&", "7"}}, "q=dogs&q=%26&q=7", "q=dogs&q=%26&q=7"}, } func TestEncodeQuery(t *testing.T) { for _, tt := range encodeQueryTests { - if q := EncodeQuery(tt.m); q != tt.expected && q != tt.expected1 { + if q := tt.m.Encode(); q != tt.expected && q != tt.expected1 { t.Errorf(`EncodeQuery(%+v) = %q, want %q`, tt.m, q, tt.expected) } } @@ -673,3 +671,28 @@ func TestResolveReference(t *testing.T) { } } + +func TestQueryValues(t *testing.T) { + u, _ := ParseURL("http://x.com?foo=bar&bar=1&bar=2") + v := u.Query() + if len(v) != 2 { + t.Errorf("got %d keys in Query values, want 2", len(v)) + } + if g, e := v.Get("foo"), "bar"; g != e { + t.Errorf("Get(foo) = %q, want %q", g, e) + } + // Case sensitive: + if g, e := v.Get("Foo"), ""; g != e { + t.Errorf("Get(Foo) = %q, want %q", g, e) + } + if g, e := v.Get("bar"), "1"; g != e { + t.Errorf("Get(bar) = %q, want %q", g, e) + } + if g, e := v.Get("baz"), ""; g != e { + t.Errorf("Get(baz) = %q, want %q", g, e) + } + v.Del("bar") + if g, e := v.Get("bar"), ""; g != e { + t.Errorf("second Get(bar) = %q, want %q", g, e) + } +} diff --git a/src/pkg/exp/draw/Makefile b/src/pkg/image/draw/Makefile index 6f0f0b2f5..2ba6e7b51 100644 --- a/src/pkg/exp/draw/Makefile +++ b/src/pkg/image/draw/Makefile @@ -4,9 +4,8 @@ include ../../../Make.inc -TARG=exp/draw +TARG=image/draw GOFILES=\ draw.go\ - event.go\ include ../../../Make.pkg diff --git a/src/pkg/image/draw/clip_test.go b/src/pkg/image/draw/clip_test.go new file mode 100644 index 000000000..db40d82f5 --- /dev/null +++ b/src/pkg/image/draw/clip_test.go @@ -0,0 +1,193 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package draw + +import ( + "image" + "testing" +) + +type clipTest struct { + desc string + r, dr, sr, mr image.Rectangle + sp, mp image.Point + nilMask bool + r0 image.Rectangle + sp0, mp0 image.Point +} + +var clipTests = []clipTest{ + // The following tests all have a nil mask. + { + "basic", + image.Rect(0, 0, 100, 100), + image.Rect(0, 0, 100, 100), + image.Rect(0, 0, 100, 100), + image.ZR, + image.ZP, + image.ZP, + true, + image.Rect(0, 0, 100, 100), + image.ZP, + image.ZP, + }, + { + "clip dr", + image.Rect(0, 0, 100, 100), + image.Rect(40, 40, 60, 60), + image.Rect(0, 0, 100, 100), + image.ZR, + image.ZP, + image.ZP, + true, + image.Rect(40, 40, 60, 60), + image.Pt(40, 40), + image.ZP, + }, + { + "clip sr", + image.Rect(0, 0, 100, 100), + image.Rect(0, 0, 100, 100), + image.Rect(20, 20, 80, 80), + image.ZR, + image.ZP, + image.ZP, + true, + image.Rect(20, 20, 80, 80), + image.Pt(20, 20), + image.ZP, + }, + { + "clip dr and sr", + image.Rect(0, 0, 100, 100), + image.Rect(0, 0, 50, 100), + image.Rect(20, 20, 80, 80), + image.ZR, + image.ZP, + image.ZP, + true, + image.Rect(20, 20, 50, 80), + image.Pt(20, 20), + image.ZP, + }, + { + "clip dr and sr, sp outside sr (top-left)", + image.Rect(0, 0, 100, 100), + image.Rect(0, 0, 50, 100), + image.Rect(20, 20, 80, 80), + image.ZR, + image.Pt(15, 8), + image.ZP, + true, + image.Rect(5, 12, 50, 72), + image.Pt(20, 20), + image.ZP, + }, + { + "clip dr and sr, sp outside sr (middle-left)", + image.Rect(0, 0, 100, 100), + image.Rect(0, 0, 50, 100), + image.Rect(20, 20, 80, 80), + image.ZR, + image.Pt(15, 66), + image.ZP, + true, + image.Rect(5, 0, 50, 14), + image.Pt(20, 66), + image.ZP, + }, + { + "clip dr and sr, sp outside sr (bottom-left)", + image.Rect(0, 0, 100, 100), + image.Rect(0, 0, 50, 100), + image.Rect(20, 20, 80, 80), + image.ZR, + image.Pt(15, 91), + image.ZP, + true, + image.ZR, + image.Pt(15, 91), + image.ZP, + }, + { + "clip dr and sr, sp inside sr", + image.Rect(0, 0, 100, 100), + image.Rect(0, 0, 50, 100), + image.Rect(20, 20, 80, 80), + image.ZR, + image.Pt(44, 33), + image.ZP, + true, + image.Rect(0, 0, 36, 47), + image.Pt(44, 33), + image.ZP, + }, + + // The following tests all have a non-nil mask. + { + "basic mask", + image.Rect(0, 0, 80, 80), + image.Rect(20, 0, 100, 80), + image.Rect(0, 0, 50, 49), + image.Rect(0, 0, 46, 47), + image.ZP, + image.ZP, + false, + image.Rect(20, 0, 46, 47), + image.Pt(20, 0), + image.Pt(20, 0), + }, + // TODO(nigeltao): write more tests. +} + +func TestClip(t *testing.T) { + dst0 := image.NewRGBA(100, 100) + src0 := image.NewRGBA(100, 100) + mask0 := image.NewRGBA(100, 100) + for _, c := range clipTests { + dst := dst0.SubImage(c.dr).(*image.RGBA) + src := src0.SubImage(c.sr).(*image.RGBA) + var mask image.Image + if !c.nilMask { + mask = mask0.SubImage(c.mr) + } + r, sp, mp := c.r, c.sp, c.mp + clip(dst, &r, src, &sp, mask, &mp) + + // Check that the actual results equal the expected results. + if !c.r0.Eq(r) { + t.Errorf("%s: clip rectangle want %v got %v", c.desc, c.r0, r) + continue + } + if !c.sp0.Eq(sp) { + t.Errorf("%s: sp want %v got %v", c.desc, c.sp0, sp) + continue + } + if !c.nilMask { + if !c.mp0.Eq(mp) { + t.Errorf("%s: mp want %v got %v", c.desc, c.mp0, mp) + continue + } + } + + // Check that the clipped rectangle is contained by the dst / src / mask + // rectangles, in their respective co-ordinate spaces. + if !r.In(c.dr) { + t.Errorf("%s: c.dr %v does not contain r %v", c.desc, c.dr, r) + } + // sr is r translated into src's co-ordinate space. + sr := r.Add(c.sp.Sub(c.dr.Min)) + if !sr.In(c.sr) { + t.Errorf("%s: c.sr %v does not contain sr %v", c.desc, c.sr, sr) + } + if !c.nilMask { + // mr is r translated into mask's co-ordinate space. + mr := r.Add(c.mp.Sub(c.dr.Min)) + if !mr.In(c.mr) { + t.Errorf("%s: c.mr %v does not contain mr %v", c.desc, c.mr, mr) + } + } + } +} diff --git a/src/pkg/exp/draw/draw.go b/src/pkg/image/draw/draw.go index f98e24618..618fb4aa6 100644 --- a/src/pkg/exp/draw/draw.go +++ b/src/pkg/image/draw/draw.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package draw provides basic graphics and drawing primitives, +// Package draw provides image composition functions // in the style of the Plan 9 graphics library // (see http://plan9.bell-labs.com/magic/man2html/2/draw) // and the X Render extension. @@ -16,7 +16,7 @@ import ( // m is the maximum color value returned by image.Color.RGBA. const m = 1<<16 - 1 -// A Porter-Duff compositing operator. +// Op is a Porter-Duff compositing operator. type Op int const ( @@ -39,27 +39,31 @@ func Draw(dst Image, r image.Rectangle, src image.Image, sp image.Point) { DrawMask(dst, r, src, sp, nil, image.ZP, Over) } -// DrawMask aligns r.Min in dst with sp in src and mp in mask and then replaces the rectangle r -// in dst with the result of a Porter-Duff composition. A nil mask is treated as opaque. -func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) { - sb := src.Bounds() - dx, dy := sb.Max.X-sp.X, sb.Max.Y-sp.Y +// clip clips r against each image's bounds (after translating into the +// destination image's co-ordinate space) and shifts the points sp and mp by +// the same amount as the change in r.Min. +func clip(dst Image, r *image.Rectangle, src image.Image, sp *image.Point, mask image.Image, mp *image.Point) { + orig := r.Min + *r = r.Intersect(dst.Bounds()) + *r = r.Intersect(src.Bounds().Add(orig.Sub(*sp))) if mask != nil { - mb := mask.Bounds() - if dx > mb.Max.X-mp.X { - dx = mb.Max.X - mp.X - } - if dy > mb.Max.Y-mp.Y { - dy = mb.Max.Y - mp.Y - } - } - if r.Dx() > dx { - r.Max.X = r.Min.X + dx + *r = r.Intersect(mask.Bounds().Add(orig.Sub(*mp))) } - if r.Dy() > dy { - r.Max.Y = r.Min.Y + dy + dx := r.Min.X - orig.X + dy := r.Min.Y - orig.Y + if dx == 0 && dy == 0 { + return } - r = r.Intersect(dst.Bounds()) + (*sp).X += dx + (*sp).Y += dy + (*mp).X += dx + (*mp).Y += dy +} + +// DrawMask aligns r.Min in dst with sp in src and mp in mask and then replaces the rectangle r +// in dst with the result of a Porter-Duff composition. A nil mask is treated as opaque. +func DrawMask(dst Image, r image.Rectangle, src image.Image, sp image.Point, mask image.Image, mp image.Point, op Op) { + clip(dst, &r, src, &sp, mask, &mp) if r.Empty() { return } diff --git a/src/pkg/exp/draw/draw_test.go b/src/pkg/image/draw/draw_test.go index 873a2f24a..37d630353 100644 --- a/src/pkg/exp/draw/draw_test.go +++ b/src/pkg/image/draw/draw_test.go @@ -263,8 +263,8 @@ func TestDrawOverlap(t *testing.T) { } } -// TestIssue836 verifies http://code.google.com/p/go/issues/detail?id=836. -func TestIssue836(t *testing.T) { +// TestNonZeroSrcPt checks drawing with a non-zero src point parameter. +func TestNonZeroSrcPt(t *testing.T) { a := image.NewRGBA(1, 1) b := image.NewRGBA(2, 2) b.Set(0, 0, image.RGBAColor{0, 0, 0, 5}) @@ -273,6 +273,6 @@ func TestIssue836(t *testing.T) { b.Set(1, 1, image.RGBAColor{5, 0, 0, 5}) Draw(a, image.Rect(0, 0, 1, 1), b, image.Pt(1, 1)) if !eq(image.RGBAColor{5, 0, 0, 5}, a.At(0, 0)) { - t.Errorf("Issue 836: want %v got %v", image.RGBAColor{5, 0, 0, 5}, a.At(0, 0)) + t.Errorf("non-zero src pt: want %v got %v", image.RGBAColor{5, 0, 0, 5}, a.At(0, 0)) } } diff --git a/src/pkg/image/geom.go b/src/pkg/image/geom.go index ccfe9cdb0..667aee625 100644 --- a/src/pkg/image/geom.go +++ b/src/pkg/image/geom.go @@ -38,6 +38,12 @@ func (p Point) Div(k int) Point { return Point{p.X / k, p.Y / k} } +// In returns whether p is in r. +func (p Point) In(r Rectangle) bool { + return r.Min.X <= p.X && p.X < r.Max.X && + r.Min.Y <= p.Y && p.Y < r.Max.Y +} + // Mod returns the point q in r such that p.X-q.X is a multiple of r's width // and p.Y-q.Y is a multiple of r's height. func (p Point) Mod(r Rectangle) Point { @@ -190,10 +196,15 @@ func (r Rectangle) Overlaps(s Rectangle) bool { r.Min.Y < s.Max.Y && s.Min.Y < r.Max.Y } -// Contains returns whether r contains p. -func (r Rectangle) Contains(p Point) bool { - return p.X >= r.Min.X && p.X < r.Max.X && - p.Y >= r.Min.Y && p.Y < r.Max.Y +// In returns whether every point in r is in s. +func (r Rectangle) In(s Rectangle) bool { + if r.Empty() { + return true + } + // Note that r.Max is an exclusive bound for r, so that r.In(s) + // does not require that r.Max.In(s). + return s.Min.X <= r.Min.X && r.Max.X <= s.Max.X && + s.Min.Y <= r.Min.Y && r.Max.Y <= s.Max.Y } // Canon returns the canonical version of r. The returned rectangle has minimum diff --git a/src/pkg/image/gif/reader.go b/src/pkg/image/gif/reader.go index 9f7296a98..26c013b9a 100644 --- a/src/pkg/image/gif/reader.go +++ b/src/pkg/image/gif/reader.go @@ -274,7 +274,7 @@ func (d *decoder) readExtension() os.Error { return fmt.Errorf("gif: unknown extension 0x%.2x", extension) } if size > 0 { - if _, err := d.r.Read(d.tmp[0:size]); err != nil { + if _, err := io.ReadFull(d.r, d.tmp[0:size]); err != nil { return err } } @@ -406,8 +406,8 @@ func DecodeAll(r io.Reader) (*GIF, os.Error) { return gif, nil } -// DecodeConfig returns the color model and dimensions of a GIF image without -// decoding the entire image. +// DecodeConfig returns the global color model and dimensions of a GIF image +// without decoding the entire image. func DecodeConfig(r io.Reader) (image.Config, os.Error) { var d decoder if err := d.decode(r, true); err != nil { diff --git a/src/pkg/image/image.go b/src/pkg/image/image.go index 1bdac36f5..f4c38d28a 100644 --- a/src/pkg/image/image.go +++ b/src/pkg/image/image.go @@ -38,21 +38,21 @@ func (p *RGBA) ColorModel() ColorModel { return RGBAColorModel } func (p *RGBA) Bounds() Rectangle { return p.Rect } func (p *RGBA) At(x, y int) Color { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return RGBAColor{} } return p.Pix[y*p.Stride+x] } func (p *RGBA) Set(x, y int, c Color) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = toRGBAColor(c).(RGBAColor) } func (p *RGBA) SetRGBA(x, y int, c RGBAColor) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = c @@ -107,21 +107,21 @@ func (p *RGBA64) ColorModel() ColorModel { return RGBA64ColorModel } func (p *RGBA64) Bounds() Rectangle { return p.Rect } func (p *RGBA64) At(x, y int) Color { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return RGBA64Color{} } return p.Pix[y*p.Stride+x] } func (p *RGBA64) Set(x, y int, c Color) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = toRGBA64Color(c).(RGBA64Color) } func (p *RGBA64) SetRGBA64(x, y int, c RGBA64Color) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = c @@ -176,21 +176,21 @@ func (p *NRGBA) ColorModel() ColorModel { return NRGBAColorModel } func (p *NRGBA) Bounds() Rectangle { return p.Rect } func (p *NRGBA) At(x, y int) Color { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return NRGBAColor{} } return p.Pix[y*p.Stride+x] } func (p *NRGBA) Set(x, y int, c Color) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = toNRGBAColor(c).(NRGBAColor) } func (p *NRGBA) SetNRGBA(x, y int, c NRGBAColor) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = c @@ -245,21 +245,21 @@ func (p *NRGBA64) ColorModel() ColorModel { return NRGBA64ColorModel } func (p *NRGBA64) Bounds() Rectangle { return p.Rect } func (p *NRGBA64) At(x, y int) Color { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return NRGBA64Color{} } return p.Pix[y*p.Stride+x] } func (p *NRGBA64) Set(x, y int, c Color) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = toNRGBA64Color(c).(NRGBA64Color) } func (p *NRGBA64) SetNRGBA64(x, y int, c NRGBA64Color) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = c @@ -314,21 +314,21 @@ func (p *Alpha) ColorModel() ColorModel { return AlphaColorModel } func (p *Alpha) Bounds() Rectangle { return p.Rect } func (p *Alpha) At(x, y int) Color { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return AlphaColor{} } return p.Pix[y*p.Stride+x] } func (p *Alpha) Set(x, y int, c Color) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = toAlphaColor(c).(AlphaColor) } func (p *Alpha) SetAlpha(x, y int, c AlphaColor) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = c @@ -383,21 +383,21 @@ func (p *Alpha16) ColorModel() ColorModel { return Alpha16ColorModel } func (p *Alpha16) Bounds() Rectangle { return p.Rect } func (p *Alpha16) At(x, y int) Color { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return Alpha16Color{} } return p.Pix[y*p.Stride+x] } func (p *Alpha16) Set(x, y int, c Color) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = toAlpha16Color(c).(Alpha16Color) } func (p *Alpha16) SetAlpha16(x, y int, c Alpha16Color) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = c @@ -452,21 +452,21 @@ func (p *Gray) ColorModel() ColorModel { return GrayColorModel } func (p *Gray) Bounds() Rectangle { return p.Rect } func (p *Gray) At(x, y int) Color { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return GrayColor{} } return p.Pix[y*p.Stride+x] } func (p *Gray) Set(x, y int, c Color) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = toGrayColor(c).(GrayColor) } func (p *Gray) SetGray(x, y int, c GrayColor) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = c @@ -507,21 +507,21 @@ func (p *Gray16) ColorModel() ColorModel { return Gray16ColorModel } func (p *Gray16) Bounds() Rectangle { return p.Rect } func (p *Gray16) At(x, y int) Color { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return Gray16Color{} } return p.Pix[y*p.Stride+x] } func (p *Gray16) Set(x, y int, c Color) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = toGray16Color(c).(Gray16Color) } func (p *Gray16) SetGray16(x, y int, c Gray16Color) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = c @@ -563,14 +563,19 @@ func (p PalettedColorModel) Convert(c Color) Color { if len(p) == 0 { return nil } + return p[p.Index(c)] +} + +// Index returns the index of the palette color closest to c in Euclidean +// R,G,B space. +func (p PalettedColorModel) Index(c Color) int { cr, cg, cb, _ := c.RGBA() // Shift by 1 bit to avoid potential uint32 overflow in sum-squared-difference. cr >>= 1 cg >>= 1 cb >>= 1 - result := Color(nil) - bestSSD := uint32(1<<32 - 1) - for _, v := range p { + ret, bestSSD := 0, uint32(1<<32-1) + for i, v := range p { vr, vg, vb, _ := v.RGBA() vr >>= 1 vg >>= 1 @@ -578,11 +583,10 @@ func (p PalettedColorModel) Convert(c Color) Color { dr, dg, db := diff(cr, vr), diff(cg, vg), diff(cb, vb) ssd := (dr * dr) + (dg * dg) + (db * db) if ssd < bestSSD { - bestSSD = ssd - result = v + ret, bestSSD = i, ssd } } - return result + return ret } // A Paletted is an in-memory image backed by a 2-D slice of uint8 values and a PalettedColorModel. @@ -604,21 +608,28 @@ func (p *Paletted) At(x, y int) Color { if len(p.Palette) == 0 { return nil } - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return p.Palette[0] } return p.Palette[p.Pix[y*p.Stride+x]] } +func (p *Paletted) Set(x, y int, c Color) { + if !(Point{x, y}.In(p.Rect)) { + return + } + p.Pix[y*p.Stride+x] = uint8(p.Palette.Index(c)) +} + func (p *Paletted) ColorIndexAt(x, y int) uint8 { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return 0 } return p.Pix[y*p.Stride+x] } func (p *Paletted) SetColorIndex(x, y int, index uint8) { - if !p.Rect.Contains(Point{x, y}) { + if !(Point{x, y}.In(p.Rect)) { return } p.Pix[y*p.Stride+x] = index diff --git a/src/pkg/image/image_test.go b/src/pkg/image/image_test.go new file mode 100644 index 000000000..17e314795 --- /dev/null +++ b/src/pkg/image/image_test.go @@ -0,0 +1,71 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package image + +import ( + "testing" +) + +func cmp(t *testing.T, cm ColorModel, c0, c1 Color) bool { + r0, g0, b0, a0 := cm.Convert(c0).RGBA() + r1, g1, b1, a1 := cm.Convert(c1).RGBA() + return r0 == r1 && g0 == g1 && b0 == b1 && a0 == a1 +} + +func TestImage(t *testing.T) { + type buffered interface { + Image + Set(int, int, Color) + SubImage(Rectangle) Image + } + testImage := []Image{ + NewRGBA(10, 10), + NewRGBA64(10, 10), + NewNRGBA(10, 10), + NewNRGBA64(10, 10), + NewAlpha(10, 10), + NewAlpha16(10, 10), + NewGray(10, 10), + NewGray16(10, 10), + NewPaletted(10, 10, PalettedColorModel{ + Transparent, + Opaque, + }), + } + for _, m := range testImage { + b := m.(buffered) + if !Rect(0, 0, 10, 10).Eq(b.Bounds()) { + t.Errorf("%T: want bounds %v, got %v", b, Rect(0, 0, 10, 10), b.Bounds()) + continue + } + if !cmp(t, b.ColorModel(), Transparent, b.At(6, 3)) { + t.Errorf("%T: at (6, 3), want a zero color, got %v", b, b.At(6, 3)) + continue + } + b.Set(6, 3, Opaque) + if !cmp(t, b.ColorModel(), Opaque, b.At(6, 3)) { + t.Errorf("%T: at (6, 3), want a non-zero color, got %v", b, b.At(6, 3)) + continue + } + b = b.SubImage(Rect(3, 2, 9, 8)).(buffered) + if !Rect(3, 2, 9, 8).Eq(b.Bounds()) { + t.Errorf("%T: sub-image want bounds %v, got %v", b, Rect(3, 2, 9, 8), b.Bounds()) + continue + } + if !cmp(t, b.ColorModel(), Opaque, b.At(6, 3)) { + t.Errorf("%T: sub-image at (6, 3), want a non-zero color, got %v", b, b.At(6, 3)) + continue + } + if !cmp(t, b.ColorModel(), Transparent, b.At(3, 3)) { + t.Errorf("%T: sub-image at (3, 3), want a zero color, got %v", b, b.At(3, 3)) + continue + } + b.Set(3, 3, Opaque) + if !cmp(t, b.ColorModel(), Opaque, b.At(3, 3)) { + t.Errorf("%T: sub-image at (3, 3), want a non-zero color, got %v", b, b.At(3, 3)) + continue + } + } +} diff --git a/src/pkg/image/tiff/reader.go b/src/pkg/image/tiff/reader.go index 57a7be4a2..26e52144d 100644 --- a/src/pkg/image/tiff/reader.go +++ b/src/pkg/image/tiff/reader.go @@ -46,6 +46,11 @@ type decoder struct { mode imageMode features map[int][]uint palette []image.Color + + buf []byte + off int // Current offset in buf. + v uint32 // Buffer value for reading with arbitrary bit depths. + nbits uint // Remaining number of bits in v. } // firstVal returns the first uint of the features entry with the given tag, @@ -151,78 +156,94 @@ func (d *decoder) parseIFD(p []byte) os.Error { return nil } -// decode decodes the raw data of an image with 8 bits in each sample. -// It reads from p and writes the strip with ymin <= y < ymax into dst. -func (d *decoder) decode(dst image.Image, p []byte, ymin, ymax int) os.Error { +// readBits reads n bits from the internal buffer starting at the current offset. +func (d *decoder) readBits(n uint) uint32 { + for d.nbits < n { + d.v <<= 8 + d.v |= uint32(d.buf[d.off]) + d.off++ + d.nbits += 8 + } + d.nbits -= n + rv := d.v >> d.nbits + d.v &^= rv << d.nbits + return rv +} + +// flushBits discards the unread bits in the buffer used by readBits. +// It is used at the end of a line. +func (d *decoder) flushBits() { + d.v = 0 + d.nbits = 0 +} + +// decode decodes the raw data of an image. +// It reads from d.buf and writes the strip with ymin <= y < ymax into dst. +func (d *decoder) decode(dst image.Image, ymin, ymax int) os.Error { spp := len(d.features[tBitsPerSample]) // samples per pixel - off := 0 + d.off = 0 width := dst.Bounds().Dx() - if len(p) < spp*(ymax-ymin)*width { - return FormatError("short data strip") - } - // Apply horizontal predictor if necessary. // In this case, p contains the color difference to the preceding pixel. // See page 64-65 of the spec. - if d.firstVal(tPredictor) == prHorizontal { + if d.firstVal(tPredictor) == prHorizontal && d.firstVal(tBitsPerSample) == 8 { for y := ymin; y < ymax; y++ { - off += spp + d.off += spp for x := 0; x < (width-1)*spp; x++ { - p[off] += p[off-spp] - off++ + d.buf[d.off] += d.buf[d.off-spp] + d.off++ } } - off = 0 + d.off = 0 } switch d.mode { - case mGray: - img := dst.(*image.Gray) - for y := ymin; y < ymax; y++ { - for x := img.Rect.Min.X; x < img.Rect.Max.X; x++ { - img.Set(x, y, image.GrayColor{p[off]}) - off += spp - } - } - case mGrayInvert: + case mGray, mGrayInvert: img := dst.(*image.Gray) + bpp := d.firstVal(tBitsPerSample) + max := uint32((1 << bpp) - 1) for y := ymin; y < ymax; y++ { for x := img.Rect.Min.X; x < img.Rect.Max.X; x++ { - img.Set(x, y, image.GrayColor{0xff - p[off]}) - off += spp + v := uint8(d.readBits(bpp) * 0xff / max) + if d.mode == mGrayInvert { + v = 0xff - v + } + img.SetGray(x, y, image.GrayColor{v}) } + d.flushBits() } case mPaletted: img := dst.(*image.Paletted) + bpp := d.firstVal(tBitsPerSample) for y := ymin; y < ymax; y++ { for x := img.Rect.Min.X; x < img.Rect.Max.X; x++ { - img.SetColorIndex(x, y, p[off]) - off += spp + img.SetColorIndex(x, y, uint8(d.readBits(bpp))) } + d.flushBits() } case mRGB: img := dst.(*image.RGBA) for y := ymin; y < ymax; y++ { for x := img.Rect.Min.X; x < img.Rect.Max.X; x++ { - img.Set(x, y, image.RGBAColor{p[off], p[off+1], p[off+2], 0xff}) - off += spp + img.SetRGBA(x, y, image.RGBAColor{d.buf[d.off], d.buf[d.off+1], d.buf[d.off+2], 0xff}) + d.off += spp } } case mNRGBA: img := dst.(*image.NRGBA) for y := ymin; y < ymax; y++ { for x := img.Rect.Min.X; x < img.Rect.Max.X; x++ { - img.Set(x, y, image.NRGBAColor{p[off], p[off+1], p[off+2], p[off+3]}) - off += spp + img.SetNRGBA(x, y, image.NRGBAColor{d.buf[d.off], d.buf[d.off+1], d.buf[d.off+2], d.buf[d.off+3]}) + d.off += spp } } case mRGBA: img := dst.(*image.RGBA) for y := ymin; y < ymax; y++ { for x := img.Rect.Min.X; x < img.Rect.Max.X; x++ { - img.Set(x, y, image.RGBAColor{p[off], p[off+1], p[off+2], p[off+3]}) - off += spp + img.SetRGBA(x, y, image.RGBAColor{d.buf[d.off], d.buf[d.off+1], d.buf[d.off+2], d.buf[d.off+3]}) + d.off += spp } } } @@ -272,9 +293,18 @@ func newDecoder(r io.Reader) (*decoder, os.Error) { d.config.Width = int(d.firstVal(tImageWidth)) d.config.Height = int(d.firstVal(tImageLength)) + if _, ok := d.features[tBitsPerSample]; !ok { + return nil, FormatError("BitsPerSample tag missing") + } + // Determine the image mode. switch d.firstVal(tPhotometricInterpretation) { case pRGB: + for _, b := range d.features[tBitsPerSample] { + if b != 8 { + return nil, UnsupportedError("non-8-bit RGB image") + } + } d.config.ColorModel = image.RGBAColorModel // RGB images normally have 3 samples per pixel. // If there are more, ExtraSamples (p. 31-32 of the spec) @@ -309,15 +339,6 @@ func newDecoder(r io.Reader) (*decoder, os.Error) { return nil, UnsupportedError("color model") } - if _, ok := d.features[tBitsPerSample]; !ok { - return nil, FormatError("BitsPerSample tag missing") - } - for _, b := range d.features[tBitsPerSample] { - if b != 8 { - return nil, UnsupportedError("not an 8-bit image") - } - } - return d, nil } @@ -357,7 +378,6 @@ func Decode(r io.Reader) (img image.Image, err os.Error) { img = image.NewRGBA(d.config.Width, d.config.Height) } - var p []byte for i := 0; i < numStrips; i++ { ymin := i * rps // The last strip may be shorter. @@ -369,18 +389,18 @@ func Decode(r io.Reader) (img image.Image, err os.Error) { switch d.firstVal(tCompression) { case cNone: // TODO(bsiegert): Avoid copy if r is a tiff.buffer. - p = make([]byte, 0, n) - _, err = d.r.ReadAt(p, offset) + d.buf = make([]byte, n) + _, err = d.r.ReadAt(d.buf, offset) case cLZW: r := lzw.NewReader(io.NewSectionReader(d.r, offset, n), lzw.MSB, 8) - p, err = ioutil.ReadAll(r) + d.buf, err = ioutil.ReadAll(r) r.Close() case cDeflate, cDeflateOld: r, err := zlib.NewReader(io.NewSectionReader(d.r, offset, n)) if err != nil { return nil, err } - p, err = ioutil.ReadAll(r) + d.buf, err = ioutil.ReadAll(r) r.Close() default: err = UnsupportedError("compression") @@ -388,7 +408,7 @@ func Decode(r io.Reader) (img image.Image, err os.Error) { if err != nil { return } - err = d.decode(img, p, ymin, ymin+rps) + err = d.decode(img, ymin, ymin+rps) } return } diff --git a/src/pkg/image/ycbcr/ycbcr.go b/src/pkg/image/ycbcr/ycbcr.go index c1c58b708..f2de3d6fb 100644 --- a/src/pkg/image/ycbcr/ycbcr.go +++ b/src/pkg/image/ycbcr/ycbcr.go @@ -142,7 +142,7 @@ func (p *YCbCr) Bounds() image.Rectangle { } func (p *YCbCr) At(x, y int) image.Color { - if !p.Rect.Contains(image.Point{x, y}) { + if !(image.Point{x, y}.In(p.Rect)) { return YCbCrColor{} } switch p.SubsampleRatio { diff --git a/src/pkg/mail/message.go b/src/pkg/mail/message.go index 9723863fe..754b779be 100644 --- a/src/pkg/mail/message.go +++ b/src/pkg/mail/message.go @@ -2,17 +2,42 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package mail implements parsing of mail messages according to RFC 5322. +/* +Package mail implements parsing of mail messages. + +For the most part, this package follows the syntax as specified by RFC 5322. +Notable divergences: + * Obsolete address formats are not parsed, including addresses with + embedded route information. + * Group addresses are not parsed. + * The full range of spacing (the CFWS syntax element) is not supported, + such as breaking addresses across lines. +*/ package mail import ( "bufio" + "bytes" + "fmt" "io" + "log" "net/textproto" "os" + "strconv" + "strings" "time" ) +var debug = debugT(false) + +type debugT bool + +func (d debugT) Printf(format string, args ...interface{}) { + if d { + log.Printf(format, args...) + } +} + // A Message represents a parsed mail message. type Message struct { Header Header @@ -72,8 +97,6 @@ func parseDate(date string) (*time.Time, os.Error) { return nil, os.ErrorString("mail: header could not be parsed") } -// TODO(dsymonds): Parsers for more specific headers such as To, From, etc. - // A Header represents the key-value pairs in a mail message header. type Header map[string][]string @@ -93,3 +116,376 @@ func (h Header) Date() (*time.Time, os.Error) { } return parseDate(hdr) } + +// AddressList parses the named header field as a list of addresses. +func (h Header) AddressList(key string) ([]*Address, os.Error) { + hdr := h.Get(key) + if hdr == "" { + return nil, ErrHeaderNotPresent + } + return newAddrParser(hdr).parseAddressList() +} + +// Address represents a single mail address. +// An address such as "Barry Gibbs <bg@example.com>" is represented +// as Address{Name: "Barry Gibbs", Address: "bg@example.com"}. +type Address struct { + Name string // Proper name; may be empty. + Address string // user@domain +} + +// String formats the address as a valid RFC 5322 address. +// If the address's name contains non-ASCII characters +// the name will be rendered according to RFC 2047. +func (a *Address) String() string { + s := "<" + a.Address + ">" + if a.Name == "" { + return s + } + // If every character is printable ASCII, quoting is simple. + allPrintable := true + for i := 0; i < len(a.Name); i++ { + if !isVchar(a.Name[i]) { + allPrintable = false + break + } + } + if allPrintable { + b := bytes.NewBufferString(`"`) + for i := 0; i < len(a.Name); i++ { + if !isQtext(a.Name[i]) { + b.WriteByte('\\') + } + b.WriteByte(a.Name[i]) + } + b.WriteString(`" `) + b.WriteString(s) + return b.String() + } + + // UTF-8 "Q" encoding + b := bytes.NewBufferString("=?utf-8?q?") + for i := 0; i < len(a.Name); i++ { + switch c := a.Name[i]; { + case c == ' ': + b.WriteByte('_') + case isVchar(c) && c != '=' && c != '?' && c != '_': + b.WriteByte(c) + default: + fmt.Fprintf(b, "=%02X", c) + } + } + b.WriteString("?= ") + b.WriteString(s) + return b.String() +} + +type addrParser []byte + +func newAddrParser(s string) *addrParser { + p := addrParser([]byte(s)) + return &p +} + +func (p *addrParser) parseAddressList() ([]*Address, os.Error) { + var list []*Address + for { + p.skipSpace() + addr, err := p.parseAddress() + if err != nil { + return nil, err + } + list = append(list, addr) + + p.skipSpace() + if p.empty() { + break + } + if !p.consume(',') { + return nil, os.ErrorString("mail: expected comma") + } + } + return list, nil +} + +// parseAddress parses a single RFC 5322 address at the start of p. +func (p *addrParser) parseAddress() (addr *Address, err os.Error) { + debug.Printf("parseAddress: %q", *p) + p.skipSpace() + if p.empty() { + return nil, os.ErrorString("mail: no address") + } + + // address = name-addr / addr-spec + // TODO(dsymonds): Support parsing group address. + + // addr-spec has a more restricted grammar than name-addr, + // so try parsing it first, and fallback to name-addr. + // TODO(dsymonds): Is this really correct? + spec, err := p.consumeAddrSpec() + if err == nil { + return &Address{ + Address: spec, + }, err + } + debug.Printf("parseAddress: not an addr-spec: %v", err) + debug.Printf("parseAddress: state is now %q", *p) + + // display-name + var displayName string + if p.peek() != '<' { + displayName, err = p.consumePhrase() + if err != nil { + return nil, err + } + } + debug.Printf("parseAddress: displayName=%q", displayName) + + // angle-addr = "<" addr-spec ">" + p.skipSpace() + if !p.consume('<') { + return nil, os.ErrorString("mail: no angle-addr") + } + spec, err = p.consumeAddrSpec() + if err != nil { + return nil, err + } + if !p.consume('>') { + return nil, os.ErrorString("mail: unclosed angle-addr") + } + debug.Printf("parseAddress: spec=%q", spec) + + return &Address{ + Name: displayName, + Address: spec, + }, nil +} + +// consumeAddrSpec parses a single RFC 5322 addr-spec at the start of p. +func (p *addrParser) consumeAddrSpec() (spec string, err os.Error) { + debug.Printf("consumeAddrSpec: %q", *p) + + orig := *p + defer func() { + if err != nil { + *p = orig + } + }() + + // local-part = dot-atom / quoted-string + var localPart string + p.skipSpace() + if p.empty() { + return "", os.ErrorString("mail: no addr-spec") + } + if p.peek() == '"' { + // quoted-string + debug.Printf("consumeAddrSpec: parsing quoted-string") + localPart, err = p.consumeQuotedString() + } else { + // dot-atom + debug.Printf("consumeAddrSpec: parsing dot-atom") + localPart, err = p.consumeAtom(true) + } + if err != nil { + debug.Printf("consumeAddrSpec: failed: %v", err) + return "", err + } + + if !p.consume('@') { + return "", os.ErrorString("mail: missing @ in addr-spec") + } + + // domain = dot-atom / domain-literal + var domain string + p.skipSpace() + if p.empty() { + return "", os.ErrorString("mail: no domain in addr-spec") + } + // TODO(dsymonds): Handle domain-literal + domain, err = p.consumeAtom(true) + if err != nil { + return "", err + } + + return localPart + "@" + domain, nil +} + +// consumePhrase parses the RFC 5322 phrase at the start of p. +func (p *addrParser) consumePhrase() (phrase string, err os.Error) { + debug.Printf("consumePhrase: [%s]", *p) + // phrase = 1*word + var words []string + for { + // word = atom / quoted-string + var word string + p.skipSpace() + if p.empty() { + return "", os.ErrorString("mail: missing phrase") + } + if p.peek() == '"' { + // quoted-string + word, err = p.consumeQuotedString() + } else { + // atom + word, err = p.consumeAtom(false) + } + + // RFC 2047 encoded-word starts with =?, ends with ?=, and has two other ?s. + if err == nil && strings.HasPrefix(word, "=?") && strings.HasSuffix(word, "?=") && strings.Count(word, "?") == 4 { + word, err = decodeRFC2047Word(word) + } + + if err != nil { + break + } + debug.Printf("consumePhrase: consumed %q", word) + words = append(words, word) + } + // Ignore any error if we got at least one word. + if err != nil && len(words) == 0 { + debug.Printf("consumePhrase: hit err: %v", err) + return "", os.ErrorString("mail: missing word in phrase") + } + phrase = strings.Join(words, " ") + return phrase, nil +} + +// consumeQuotedString parses the quoted string at the start of p. +func (p *addrParser) consumeQuotedString() (qs string, err os.Error) { + // Assume first byte is '"'. + i := 1 + qsb := make([]byte, 0, 10) +Loop: + for { + if i >= p.len() { + return "", os.ErrorString("mail: unclosed quoted-string") + } + switch c := (*p)[i]; { + case c == '"': + break Loop + case c == '\\': + if i+1 == p.len() { + return "", os.ErrorString("mail: unclosed quoted-string") + } + qsb = append(qsb, (*p)[i+1]) + i += 2 + case isQtext(c), c == ' ' || c == '\t': + // qtext (printable US-ASCII excluding " and \), or + // FWS (almost; we're ignoring CRLF) + qsb = append(qsb, c) + i++ + default: + return "", fmt.Errorf("mail: bad character in quoted-string: %q", c) + } + } + *p = (*p)[i+1:] + return string(qsb), nil +} + +// consumeAtom parses an RFC 5322 atom at the start of p. +// If dot is true, consumeAtom parses an RFC 5322 dot-atom instead. +func (p *addrParser) consumeAtom(dot bool) (atom string, err os.Error) { + if !isAtext(p.peek(), false) { + return "", os.ErrorString("mail: invalid string") + } + i := 1 + for ; i < p.len() && isAtext((*p)[i], dot); i++ { + } + // TODO(dsymonds): Remove the []byte() conversion here when 6g doesn't need it. + atom, *p = string([]byte((*p)[:i])), (*p)[i:] + return atom, nil +} + +func (p *addrParser) consume(c byte) bool { + if p.empty() || p.peek() != c { + return false + } + *p = (*p)[1:] + return true +} + +// skipSpace skips the leading space and tab characters. +func (p *addrParser) skipSpace() { + *p = bytes.TrimLeft(*p, " \t") +} + +func (p *addrParser) peek() byte { + return (*p)[0] +} + +func (p *addrParser) empty() bool { + return p.len() == 0 +} + +func (p *addrParser) len() int { + return len(*p) +} + +func decodeRFC2047Word(s string) (string, os.Error) { + fields := strings.Split(s, "?", -1) + if len(fields) != 5 || fields[0] != "=" || fields[4] != "=" { + return "", os.ErrorString("mail: address not RFC 2047 encoded") + } + charset, enc := strings.ToLower(fields[1]), strings.ToLower(fields[2]) + // TODO(dsymonds): Support "b" encoding too. + if enc != "q" { + return "", fmt.Errorf("mail: RFC 2047 encoding not supported: %q", enc) + } + if charset != "iso-8859-1" && charset != "utf-8" { + return "", fmt.Errorf("mail: charset not supported: %q", charset) + } + + in := fields[3] + b := new(bytes.Buffer) + for i := 0; i < len(in); i++ { + switch c := in[i]; { + case c == '=' && i+2 < len(in): + x, err := strconv.Btoi64(in[i+1:i+3], 16) + if err != nil { + return "", fmt.Errorf("mail: invalid RFC 2047 encoding: %q", in[i:i+3]) + } + i += 2 + switch charset { + case "iso-8859-1": + b.WriteRune(int(x)) + case "utf-8": + b.WriteByte(byte(x)) + } + case c == '_': + b.WriteByte(' ') + default: + b.WriteByte(c) + } + } + return b.String(), nil +} + +var atextChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ" + + "abcdefghijklmnopqrstuvwxyz" + + "0123456789" + + "!#$%&'*+-/=?^_`{|}~") + +// isAtext returns true if c is an RFC 5322 atext character. +// If dot is true, period is included. +func isAtext(c byte, dot bool) bool { + if dot && c == '.' { + return true + } + return bytes.IndexByte(atextChars, c) >= 0 +} + +// isQtext returns true if c is an RFC 5322 qtest character. +func isQtext(c byte) bool { + // Printable US-ASCII, excluding backslash or quote. + if c == '\\' || c == '"' { + return false + } + return '!' <= c && c <= '~' +} + +// isVchar returns true if c is an RFC 5322 VCHAR character. +func isVchar(c byte) bool { + // Visible (printing) characters. + return '!' <= c && c <= '~' +} diff --git a/src/pkg/mail/message_test.go b/src/pkg/mail/message_test.go index 1d1c6352e..1ff45d2c1 100644 --- a/src/pkg/mail/message_test.go +++ b/src/pkg/mail/message_test.go @@ -127,3 +127,132 @@ func TestDateParsing(t *testing.T) { } } } + +func TestAddressParsing(t *testing.T) { + tests := []struct { + addrsStr string + exp []*Address + }{ + // Bare address + { + `jdoe@machine.example`, + []*Address{&Address{ + Address: "jdoe@machine.example", + }}, + }, + // RFC 5322, Appendix A.1.1 + { + `John Doe <jdoe@machine.example>`, + []*Address{&Address{ + Name: "John Doe", + Address: "jdoe@machine.example", + }}, + }, + // RFC 5322, Appendix A.1.2 + { + `"Joe Q. Public" <john.q.public@example.com>`, + []*Address{&Address{ + Name: "Joe Q. Public", + Address: "john.q.public@example.com", + }}, + }, + { + `Mary Smith <mary@x.test>, jdoe@example.org, Who? <one@y.test>`, + []*Address{ + &Address{ + Name: "Mary Smith", + Address: "mary@x.test", + }, + &Address{ + Address: "jdoe@example.org", + }, + &Address{ + Name: "Who?", + Address: "one@y.test", + }, + }, + }, + { + `<boss@nil.test>, "Giant; \"Big\" Box" <sysservices@example.net>`, + []*Address{ + &Address{ + Address: "boss@nil.test", + }, + &Address{ + Name: `Giant; "Big" Box`, + Address: "sysservices@example.net", + }, + }, + }, + // RFC 5322, Appendix A.1.3 + // TODO(dsymonds): Group addresses. + + // RFC 2047 "Q"-encoded ISO-8859-1 address. + { + `=?iso-8859-1?q?J=F6rg_Doe?= <joerg@example.com>`, + []*Address{ + &Address{ + Name: `Jörg Doe`, + Address: "joerg@example.com", + }, + }, + }, + // RFC 2047 "Q"-encoded UTF-8 address. + { + `=?utf-8?q?J=C3=B6rg_Doe?= <joerg@example.com>`, + []*Address{ + &Address{ + Name: `Jörg Doe`, + Address: "joerg@example.com", + }, + }, + }, + // RFC 2047, Section 8. + { + `=?ISO-8859-1?Q?Andr=E9?= Pirard <PIRARD@vm1.ulg.ac.be>`, + []*Address{ + &Address{ + Name: `André Pirard`, + Address: "PIRARD@vm1.ulg.ac.be", + }, + }, + }, + } + for _, test := range tests { + addrs, err := newAddrParser(test.addrsStr).parseAddressList() + if err != nil { + t.Errorf("Failed parsing %q: %v", test.addrsStr, err) + continue + } + if !reflect.DeepEqual(addrs, test.exp) { + t.Errorf("Parse of %q: got %+v, want %+v", test.addrsStr, addrs, test.exp) + } + } +} + +func TestAddressFormatting(t *testing.T) { + tests := []struct { + addr *Address + exp string + }{ + { + &Address{Address: "bob@example.com"}, + "<bob@example.com>", + }, + { + &Address{Name: "Bob", Address: "bob@example.com"}, + `"Bob" <bob@example.com>`, + }, + { + // note the ö (o with an umlaut) + &Address{Name: "Böb", Address: "bob@example.com"}, + `=?utf-8?q?B=C3=B6b?= <bob@example.com>`, + }, + } + for _, test := range tests { + s := test.addr.String() + if s != test.exp { + t.Errorf("Address%+v.String() = %v, want %v", *test.addr, s, test.exp) + } + } +} diff --git a/src/pkg/math/Makefile b/src/pkg/math/Makefile index 71347b7fa..8e8e74ae4 100644 --- a/src/pkg/math/Makefile +++ b/src/pkg/math/Makefile @@ -6,6 +6,9 @@ include ../../Make.inc TARG=math +OFILES_arm=\ + sqrt_arm.$O\ + OFILES_amd64=\ exp_amd64.$O\ fabs_amd64.$O\ diff --git a/src/pkg/math/sqrt_arm.s b/src/pkg/math/sqrt_arm.s new file mode 100644 index 000000000..befbb8a89 --- /dev/null +++ b/src/pkg/math/sqrt_arm.s @@ -0,0 +1,10 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// func Sqrt(x float64) float64 +TEXT ·Sqrt(SB),7,$0 + MOVD x+0(FP),F0 + SQRTD F0,F0 + MOVD F0,r+8(FP) + RET diff --git a/src/pkg/math/sqrt_port.go b/src/pkg/math/sqrt_port.go index 83af255bf..148239bcf 100644 --- a/src/pkg/math/sqrt_port.go +++ b/src/pkg/math/sqrt_port.go @@ -141,3 +141,7 @@ func sqrtGo(x float64) float64 { ix = q>>1 + uint64(exp-1+bias)<<shift // significand + biased exponent return Float64frombits(ix) } + +func sqrtGoC(f float64, r *float64) { + *r = sqrtGo(f) +} diff --git a/src/pkg/net/Makefile b/src/pkg/net/Makefile index d4adbffc0..5472df392 100644 --- a/src/pkg/net/Makefile +++ b/src/pkg/net/Makefile @@ -10,6 +10,7 @@ GOFILES=\ dnsmsg.go\ fd_$(GOOS).go\ hosts.go\ + interface.go\ ip.go\ ipsock.go\ iprawsock.go\ @@ -27,6 +28,7 @@ GOFILES_freebsd=\ dnsconfig.go\ fd.go\ file.go\ + interface_bsd.go\ newpollserver.go\ port.go\ sendfile_stub.go\ @@ -41,6 +43,7 @@ GOFILES_darwin=\ dnsconfig.go\ fd.go\ file.go\ + interface_bsd.go\ newpollserver.go\ port.go\ sendfile_stub.go\ @@ -55,12 +58,14 @@ GOFILES_linux=\ dnsconfig.go\ fd.go\ file.go\ + interface_linux.go\ newpollserver.go\ port.go\ sendfile_linux.go\ sock_linux.go\ GOFILES_plan9=\ + interface_stub.go\ sendfile_stub.go\ ifeq ($(GOARCH),arm) @@ -75,6 +80,7 @@ endif GOFILES_windows=\ cgo_stub.go\ file_windows.go\ + interface_stub.go\ resolv_windows.go\ sendfile_stub.go\ sock_windows.go\ diff --git a/src/pkg/net/interface.go b/src/pkg/net/interface.go new file mode 100644 index 000000000..f622487ab --- /dev/null +++ b/src/pkg/net/interface.go @@ -0,0 +1,89 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Network interface identification + +package net + +import ( + "bytes" + "fmt" + "os" +) + +// A HardwareAddr represents a physical hardware address. +type HardwareAddr []byte + +func (a HardwareAddr) String() string { + var buf bytes.Buffer + for i, b := range a { + if i > 0 { + buf.WriteByte(':') + } + fmt.Fprintf(&buf, "%02x", b) + } + return buf.String() +} + +// Interface represents a mapping between network interface name +// and index. It also represents network interface facility +// information. +type Interface struct { + Index int // positive integer that starts at one, zero is never used + MTU int // maximum transmission unit + Name string // e.g., "en0", "lo0", "eth0.100" + HardwareAddr HardwareAddr // IEEE MAC-48, EUI-48 and EUI-64 form + rawFlags int +} + +// Addrs returns interface addresses for a specific interface. +func (ifi *Interface) Addrs() ([]Addr, os.Error) { + if ifi == nil { + return nil, os.NewError("net: invalid interface") + } + return interfaceAddrTable(ifi.Index) +} + +// Interfaces returns a list of the systems's network interfaces. +func Interfaces() ([]Interface, os.Error) { + return interfaceTable(0) +} + +// InterfaceAddrs returns a list of the system's network interface +// addresses. +func InterfaceAddrs() ([]Addr, os.Error) { + return interfaceAddrTable(0) +} + +// InterfaceByIndex returns the interface specified by index. +func InterfaceByIndex(index int) (*Interface, os.Error) { + if index <= 0 { + return nil, os.NewError("net: invalid interface index") + } + ift, err := interfaceTable(index) + if err != nil { + return nil, err + } + for _, ifi := range ift { + return &ifi, nil + } + return nil, os.NewError("net: no such interface") +} + +// InterfaceByName returns the interface specified by name. +func InterfaceByName(name string) (*Interface, os.Error) { + if name == "" { + return nil, os.NewError("net: invalid interface name") + } + ift, err := interfaceTable(0) + if err != nil { + return nil, err + } + for _, ifi := range ift { + if name == ifi.Name { + return &ifi, nil + } + } + return nil, os.NewError("net: no such interface") +} diff --git a/src/pkg/net/interface_bsd.go b/src/pkg/net/interface_bsd.go new file mode 100644 index 000000000..141b95b38 --- /dev/null +++ b/src/pkg/net/interface_bsd.go @@ -0,0 +1,195 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Network interface identification for BSD variants + +package net + +import ( + "os" + "syscall" + "unsafe" +) + +// IsUp returns true if ifi is up. +func (ifi *Interface) IsUp() bool { + if ifi == nil { + return false + } + return ifi.rawFlags&syscall.IFF_UP != 0 +} + +// IsLoopback returns true if ifi is a loopback interface. +func (ifi *Interface) IsLoopback() bool { + if ifi == nil { + return false + } + return ifi.rawFlags&syscall.IFF_LOOPBACK != 0 +} + +// CanBroadcast returns true if ifi supports a broadcast access +// capability. +func (ifi *Interface) CanBroadcast() bool { + if ifi == nil { + return false + } + return ifi.rawFlags&syscall.IFF_BROADCAST != 0 +} + +// IsPointToPoint returns true if ifi belongs to a point-to-point +// link. +func (ifi *Interface) IsPointToPoint() bool { + if ifi == nil { + return false + } + return ifi.rawFlags&syscall.IFF_POINTOPOINT != 0 +} + +// CanMulticast returns true if ifi supports a multicast access +// capability. +func (ifi *Interface) CanMulticast() bool { + if ifi == nil { + return false + } + return ifi.rawFlags&syscall.IFF_MULTICAST != 0 +} + +// If the ifindex is zero, interfaceTable returns mappings of all +// network interfaces. Otheriwse it returns a mapping of a specific +// interface. +func interfaceTable(ifindex int) ([]Interface, os.Error) { + var ( + tab []byte + e int + msgs []syscall.RoutingMessage + ift []Interface + ) + + tab, e = syscall.RouteRIB(syscall.NET_RT_IFLIST, ifindex) + if e != 0 { + return nil, os.NewSyscallError("route rib", e) + } + + msgs, e = syscall.ParseRoutingMessage(tab) + if e != 0 { + return nil, os.NewSyscallError("route message", e) + } + + for _, m := range msgs { + switch v := m.(type) { + case *syscall.InterfaceMessage: + if ifindex == 0 || ifindex == int(v.Header.Index) { + ifi, err := newLink(v) + if err != nil { + return nil, err + } + ift = append(ift, ifi...) + } + } + } + + return ift, nil +} + +func newLink(m *syscall.InterfaceMessage) ([]Interface, os.Error) { + var ift []Interface + + sas, e := syscall.ParseRoutingSockaddr(m) + if e != 0 { + return nil, os.NewSyscallError("route sockaddr", e) + } + + for _, s := range sas { + switch v := s.(type) { + case *syscall.SockaddrDatalink: + // NOTE: SockaddrDatalink.Data is minimum work area, + // can be larger. + m.Data = m.Data[unsafe.Offsetof(v.Data):] + ifi := Interface{Index: int(m.Header.Index), rawFlags: int(m.Header.Flags)} + var name [syscall.IFNAMSIZ]byte + for i := 0; i < int(v.Nlen); i++ { + name[i] = byte(m.Data[i]) + } + ifi.Name = string(name[:v.Nlen]) + ifi.MTU = int(m.Header.Data.Mtu) + addr := make([]byte, v.Alen) + for i := 0; i < int(v.Alen); i++ { + addr[i] = byte(m.Data[int(v.Nlen)+i]) + } + ifi.HardwareAddr = addr[:v.Alen] + ift = append(ift, ifi) + } + } + + return ift, nil +} + +// If the ifindex is zero, interfaceAddrTable returns addresses +// for all network interfaces. Otherwise it returns addresses +// for a specific interface. +func interfaceAddrTable(ifindex int) ([]Addr, os.Error) { + var ( + tab []byte + e int + msgs []syscall.RoutingMessage + ifat []Addr + ) + + tab, e = syscall.RouteRIB(syscall.NET_RT_IFLIST, ifindex) + if e != 0 { + return nil, os.NewSyscallError("route rib", e) + } + + msgs, e = syscall.ParseRoutingMessage(tab) + if e != 0 { + return nil, os.NewSyscallError("route message", e) + } + + for _, m := range msgs { + switch v := m.(type) { + case *syscall.InterfaceAddrMessage: + if ifindex == 0 || ifindex == int(v.Header.Index) { + ifa, err := newAddr(v) + if err != nil { + return nil, err + } + ifat = append(ifat, ifa...) + } + } + } + + return ifat, nil +} + +func newAddr(m *syscall.InterfaceAddrMessage) ([]Addr, os.Error) { + var ifat []Addr + + sas, e := syscall.ParseRoutingSockaddr(m) + if e != 0 { + return nil, os.NewSyscallError("route sockaddr", e) + } + + for _, s := range sas { + var ifa IPAddr + switch v := s.(type) { + case *syscall.SockaddrInet4: + ifa.IP = IPv4(v.Addr[0], v.Addr[1], v.Addr[2], v.Addr[3]) + case *syscall.SockaddrInet6: + ifa.IP = make(IP, IPv6len) + copy(ifa.IP, v.Addr[:]) + // NOTE: KAME based IPv6 protcol stack usually embeds + // the interface index in the interface-local or link- + // local address as the kernel-internal form. + if ifa.IP.IsLinkLocalUnicast() || + ifa.IP.IsInterfaceLocalMulticast() || + ifa.IP.IsLinkLocalMulticast() { + // remove embedded scope zone ID + ifa.IP[2], ifa.IP[3] = 0, 0 + } + } + ifat = append(ifat, ifa.toAddr()) + } + + return ifat, nil +} diff --git a/src/pkg/net/interface_linux.go b/src/pkg/net/interface_linux.go new file mode 100644 index 000000000..5c9657834 --- /dev/null +++ b/src/pkg/net/interface_linux.go @@ -0,0 +1,208 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Network interface identification for Linux + +package net + +import ( + "os" + "syscall" + "unsafe" +) + +// IsUp returns true if ifi is up. +func (ifi *Interface) IsUp() bool { + if ifi == nil { + return false + } + return ifi.rawFlags&syscall.IFF_UP != 0 +} + +// IsLoopback returns true if ifi is a loopback interface. +func (ifi *Interface) IsLoopback() bool { + if ifi == nil { + return false + } + return ifi.rawFlags&syscall.IFF_LOOPBACK != 0 +} + +// CanBroadcast returns true if ifi supports a broadcast access +// capability. +func (ifi *Interface) CanBroadcast() bool { + if ifi == nil { + return false + } + return ifi.rawFlags&syscall.IFF_BROADCAST != 0 +} + +// IsPointToPoint returns true if ifi belongs to a point-to-point +// link. +func (ifi *Interface) IsPointToPoint() bool { + if ifi == nil { + return false + } + return ifi.rawFlags&syscall.IFF_POINTOPOINT != 0 +} + +// CanMulticast returns true if ifi supports a multicast access +// capability. +func (ifi *Interface) CanMulticast() bool { + if ifi == nil { + return false + } + return ifi.rawFlags&syscall.IFF_MULTICAST != 0 +} + +// If the ifindex is zero, interfaceTable returns mappings of all +// network interfaces. Otheriwse it returns a mapping of a specific +// interface. +func interfaceTable(ifindex int) ([]Interface, os.Error) { + var ( + ift []Interface + tab []byte + msgs []syscall.NetlinkMessage + e int + ) + + tab, e = syscall.NetlinkRIB(syscall.RTM_GETLINK, syscall.AF_UNSPEC) + if e != 0 { + return nil, os.NewSyscallError("netlink rib", e) + } + + msgs, e = syscall.ParseNetlinkMessage(tab) + if e != 0 { + return nil, os.NewSyscallError("netlink message", e) + } + + for _, m := range msgs { + switch m.Header.Type { + case syscall.NLMSG_DONE: + goto done + case syscall.RTM_NEWLINK: + ifim := (*syscall.IfInfomsg)(unsafe.Pointer(&m.Data[0])) + if ifindex == 0 || ifindex == int(ifim.Index) { + attrs, e := syscall.ParseNetlinkRouteAttr(&m) + if e != 0 { + return nil, os.NewSyscallError("netlink routeattr", e) + } + ifi := newLink(attrs, ifim) + ift = append(ift, ifi) + } + } + } + +done: + return ift, nil +} + +func newLink(attrs []syscall.NetlinkRouteAttr, ifim *syscall.IfInfomsg) Interface { + ifi := Interface{Index: int(ifim.Index), rawFlags: int(ifim.Flags)} + for _, a := range attrs { + switch a.Attr.Type { + case syscall.IFLA_ADDRESS: + var nonzero bool + for _, b := range a.Value { + if b != 0 { + nonzero = true + } + } + if nonzero { + ifi.HardwareAddr = a.Value[:] + } + case syscall.IFLA_IFNAME: + ifi.Name = string(a.Value[:]) + case syscall.IFLA_MTU: + ifi.MTU = int(uint32(a.Value[3])<<24 | uint32(a.Value[2])<<16 | uint32(a.Value[1])<<8 | uint32(a.Value[0])) + } + } + return ifi +} + +// If the ifindex is zero, interfaceAddrTable returns addresses +// for all network interfaces. Otherwise it returns addresses +// for a specific interface. +func interfaceAddrTable(ifindex int) ([]Addr, os.Error) { + var ( + ifat4 []Addr + ifat6 []Addr + tab []byte + msgs4 []syscall.NetlinkMessage + msgs6 []syscall.NetlinkMessage + e int + err os.Error + ) + + tab, e = syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_INET) + if e != 0 { + return nil, os.NewSyscallError("netlink rib", e) + } + msgs4, e = syscall.ParseNetlinkMessage(tab) + if e != 0 { + return nil, os.NewSyscallError("netlink message", e) + } + ifat4, err = addrTable(msgs4, ifindex) + if err != nil { + return nil, err + } + + tab, e = syscall.NetlinkRIB(syscall.RTM_GETADDR, syscall.AF_INET6) + if e != 0 { + return nil, os.NewSyscallError("netlink rib", e) + } + msgs6, e = syscall.ParseNetlinkMessage(tab) + if e != 0 { + return nil, os.NewSyscallError("netlink message", e) + } + ifat6, err = addrTable(msgs6, ifindex) + if err != nil { + return nil, err + } + + return append(ifat4, ifat6...), nil +} + +func addrTable(msgs []syscall.NetlinkMessage, ifindex int) ([]Addr, os.Error) { + var ifat []Addr + + for _, m := range msgs { + switch m.Header.Type { + case syscall.NLMSG_DONE: + goto done + case syscall.RTM_NEWADDR: + ifam := (*syscall.IfAddrmsg)(unsafe.Pointer(&m.Data[0])) + if ifindex == 0 || ifindex == int(ifam.Index) { + attrs, e := syscall.ParseNetlinkRouteAttr(&m) + if e != 0 { + return nil, os.NewSyscallError("netlink routeattr", e) + } + ifat = append(ifat, newAddr(attrs, int(ifam.Family))...) + } + } + } + +done: + return ifat, nil +} + +func newAddr(attrs []syscall.NetlinkRouteAttr, family int) []Addr { + var ifat []Addr + + for _, a := range attrs { + switch a.Attr.Type { + case syscall.IFA_ADDRESS: + ifa := IPAddr{} + switch family { + case syscall.AF_INET: + ifa.IP = IPv4(a.Value[0], a.Value[1], a.Value[2], a.Value[3]) + case syscall.AF_INET6: + ifa.IP = make(IP, IPv6len) + copy(ifa.IP, a.Value[:]) + } + ifat = append(ifat, ifa.toAddr()) + } + } + + return ifat +} diff --git a/src/pkg/net/interface_stub.go b/src/pkg/net/interface_stub.go new file mode 100644 index 000000000..feb871bb5 --- /dev/null +++ b/src/pkg/net/interface_stub.go @@ -0,0 +1,51 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Network interface identification + +package net + +import "os" + +// IsUp returns true if ifi is up. +func (ifi *Interface) IsUp() bool { + return false +} + +// IsLoopback returns true if ifi is a loopback interface. +func (ifi *Interface) IsLoopback() bool { + return false +} + +// CanBroadcast returns true if ifi supports a broadcast access +// capability. +func (ifi *Interface) CanBroadcast() bool { + return false +} + +// IsPointToPoint returns true if ifi belongs to a point-to-point +// link. +func (ifi *Interface) IsPointToPoint() bool { + return false +} + +// CanMulticast returns true if ifi supports a multicast access +// capability. +func (ifi *Interface) CanMulticast() bool { + return false +} + +// If the ifindex is zero, interfaceTable returns mappings of all +// network interfaces. Otheriwse it returns a mapping of a specific +// interface. +func interfaceTable(ifindex int) ([]Interface, os.Error) { + return nil, nil +} + +// If the ifindex is zero, interfaceAddrTable returns addresses +// for all network interfaces. Otherwise it returns addresses +// for a specific interface. +func interfaceAddrTable(ifindex int) ([]Addr, os.Error) { + return nil, nil +} diff --git a/src/pkg/net/interface_test.go b/src/pkg/net/interface_test.go new file mode 100644 index 000000000..938434623 --- /dev/null +++ b/src/pkg/net/interface_test.go @@ -0,0 +1,90 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package net + +import ( + "bytes" + "testing" +) + +func sameInterface(i, j *Interface) bool { + if i == nil || j == nil { + return false + } + if i.Index == j.Index && i.Name == j.Name && bytes.Equal(i.HardwareAddr, j.HardwareAddr) { + return true + } + return false +} + +func interfaceFlagsString(ifi *Interface) string { + fs := "<" + if ifi.IsUp() { + fs += "UP," + } + if ifi.CanBroadcast() { + fs += "BROADCAST," + } + if ifi.IsLoopback() { + fs += "LOOPBACK," + } + if ifi.IsPointToPoint() { + fs += "POINTOPOINT," + } + if ifi.CanMulticast() { + fs += "MULTICAST," + } + if len(fs) > 1 { + fs = fs[:len(fs)-1] + } + fs += ">" + return fs +} + +func TestInterfaces(t *testing.T) { + ift, err := Interfaces() + if err != nil { + t.Fatalf("Interfaces() failed: %v", err) + } + t.Logf("table: len/cap = %v/%v\n", len(ift), cap(ift)) + + for _, ifi := range ift { + ifxi, err := InterfaceByIndex(ifi.Index) + if err != nil { + t.Fatalf("InterfaceByIndex(%#q) failed: %v", ifi.Index, err) + } + if !sameInterface(ifxi, &ifi) { + t.Fatalf("InterfaceByIndex(%#q) = %v, want %v", ifi.Index, *ifxi, ifi) + } + ifxn, err := InterfaceByName(ifi.Name) + if err != nil { + t.Fatalf("InterfaceByName(%#q) failed: %v", ifi.Name, err) + } + if !sameInterface(ifxn, &ifi) { + t.Fatalf("InterfaceByName(%#q) = %v, want %v", ifi.Name, *ifxn, ifi) + } + ifat, err := ifi.Addrs() + if err != nil { + t.Fatalf("Interface.Addrs() failed: %v", err) + } + t.Logf("%s: flags %s, ifindex %v, mtu %v\n", ifi.Name, interfaceFlagsString(&ifi), ifi.Index, ifi.MTU) + for _, ifa := range ifat { + t.Logf("\tinterface address %s\n", ifa.String()) + } + t.Logf("\thardware address %v", ifi.HardwareAddr.String()) + } +} + +func TestInterfaceAddrs(t *testing.T) { + ifat, err := InterfaceAddrs() + if err != nil { + t.Fatalf("InterfaceAddrs() failed: %v", err) + } + t.Logf("table: len/cap = %v/%v\n", len(ifat), cap(ifat)) + + for _, ifa := range ifat { + t.Logf("interface address %s\n", ifa.String()) + } +} diff --git a/src/pkg/netchan/import.go b/src/pkg/netchan/import.go index 0a700ca2b..7d96228c4 100644 --- a/src/pkg/netchan/import.go +++ b/src/pkg/netchan/import.go @@ -11,6 +11,7 @@ import ( "os" "reflect" "sync" + "time" ) // Import @@ -31,6 +32,9 @@ type Importer struct { chans map[int]*netChan errors chan os.Error maxId int + mu sync.Mutex // protects remaining fields + unacked int64 // number of unacknowledged sends. + seqLock sync.Mutex // guarantees messages are in sequence, only locked under mu } // NewImporter creates a new Importer object to import a set of channels @@ -42,6 +46,7 @@ func NewImporter(conn io.ReadWriter) *Importer { imp.chans = make(map[int]*netChan) imp.names = make(map[string]*netChan) imp.errors = make(chan os.Error, 10) + imp.unacked = 0 go imp.run() return imp } @@ -80,8 +85,10 @@ func (imp *Importer) run() { for { *hdr = header{} if e := imp.decode(hdrValue); e != nil { - impLog("header:", e) - imp.shutdown() + if e != os.EOF { + impLog("header:", e) + imp.shutdown() + } return } switch hdr.PayloadType { @@ -114,6 +121,9 @@ func (imp *Importer) run() { nch := imp.getChan(hdr.Id, true) if nch != nil { nch.acked() + imp.mu.Lock() + imp.unacked-- + imp.mu.Unlock() } continue default: @@ -220,10 +230,17 @@ func (imp *Importer) ImportNValues(name string, chT interface{}, dir Dir, size, } return } + // We hold the lock during transmission to guarantee messages are + // sent in order. + imp.mu.Lock() + imp.unacked++ + imp.seqLock.Lock() + imp.mu.Unlock() if err = imp.encode(hdr, payData, val.Interface()); err != nil { impLog("error encoding client send:", err) return } + imp.seqLock.Unlock() } }() } @@ -244,3 +261,27 @@ func (imp *Importer) Hangup(name string) os.Error { nc.close() return nil } + +func (imp *Importer) unackedCount() int64 { + imp.mu.Lock() + n := imp.unacked + imp.mu.Unlock() + return n +} + +// Drain waits until all messages sent from this exporter/importer, including +// those not yet sent to any server and possibly including those sent while +// Drain was executing, have been received by the exporter. In short, it +// waits until all the importer's messages have been received. +// If the timeout (measured in nanoseconds) is positive and Drain takes +// longer than that to complete, an error is returned. +func (imp *Importer) Drain(timeout int64) os.Error { + startTime := time.Nanoseconds() + for imp.unackedCount() > 0 { + if timeout > 0 && time.Nanoseconds()-startTime >= timeout { + return os.ErrorString("timeout") + } + time.Sleep(100 * 1e6) + } + return nil +} diff --git a/src/pkg/netchan/netchan_test.go b/src/pkg/netchan/netchan_test.go index fd4d8f780..8c0f9a6e4 100644 --- a/src/pkg/netchan/netchan_test.go +++ b/src/pkg/netchan/netchan_test.go @@ -178,6 +178,16 @@ func TestExportDrain(t *testing.T) { <-done } +// Not a great test but it does at least invoke Drain. +func TestImportDrain(t *testing.T) { + exp, imp := pair(t) + expDone := make(chan bool) + go exportReceive(exp, t, expDone) + <-expDone + importSend(imp, closeCount, t, nil) + imp.Drain(0) +} + // Not a great test but it does at least invoke Sync. func TestExportSync(t *testing.T) { exp, imp := pair(t) diff --git a/src/pkg/os/Makefile b/src/pkg/os/Makefile index c781df7af..497e5a958 100644 --- a/src/pkg/os/Makefile +++ b/src/pkg/os/Makefile @@ -27,6 +27,7 @@ GOFILES_freebsd=\ sys_bsd.go\ exec_posix.go\ exec_unix.go\ + signal_unix.go\ GOFILES_darwin=\ dir_unix.go\ @@ -38,6 +39,7 @@ GOFILES_darwin=\ sys_bsd.go\ exec_posix.go\ exec_unix.go\ + signal_unix.go\ GOFILES_linux=\ dir_unix.go\ @@ -49,6 +51,7 @@ GOFILES_linux=\ sys_linux.go\ exec_posix.go\ exec_unix.go\ + signal_unix.go\ GOFILES_windows=\ dir_windows.go\ @@ -60,6 +63,7 @@ GOFILES_windows=\ sys_windows.go\ exec_posix.go\ exec_windows.go\ + signal_windows.go\ GOFILES_plan9=\ dir_plan9.go\ @@ -72,4 +76,12 @@ GOFILES_plan9=\ GOFILES+=$(GOFILES_$(GOOS)) +CLEANFILES+=signal_unix.go signal_windows.go + include ../../Make.pkg + +signal_unix.go: ../syscall/zerrors_$(GOOS)_$(GOARCH).go + ./mkunixsignals.sh $< > $@ || rm -f $@ + +signal_windows.go: ../syscall/ztypes_$(GOOS)_$(GOARCH).go + ./mkunixsignals.sh $< > $@ || rm -f $@ diff --git a/src/pkg/os/env_plan9.go b/src/pkg/os/env_plan9.go index 14df55ed0..1fed89f92 100644 --- a/src/pkg/os/env_plan9.go +++ b/src/pkg/os/env_plan9.go @@ -23,13 +23,18 @@ func Getenverror(key string) (value string, err Error) { } defer f.Close() - var buf [4096]byte - n, e := f.Read(buf[:len(buf)-1]) + l, _ := f.Seek(0, 2) + f.Seek(0, 0) + buf := make([]byte, l) + n, e := f.Read(buf) if iserror(e) { return "", ENOENV } - buf[n] = 0 - return string(buf[0:n]), nil + + if n > 0 && buf[n-1] == 0 { + buf = buf[:n-1] + } + return string(buf), nil } // Getenv retrieves the value of the environment variable named by the key. @@ -52,7 +57,7 @@ func Setenv(key, value string) Error { } defer f.Close() - _, e = f.Write(syscall.StringByteSlice(value)) + _, e = f.Write([]byte(value)) return nil } diff --git a/src/pkg/os/error_posix.go b/src/pkg/os/error_posix.go index 0ee34e4b0..d43f1786d 100644 --- a/src/pkg/os/error_posix.go +++ b/src/pkg/os/error_posix.go @@ -13,7 +13,7 @@ type Errno int64 func (e Errno) String() string { return syscall.Errstr(int(e)) } func (e Errno) Temporary() bool { - return e == Errno(syscall.EINTR) || e.Timeout() + return e == Errno(syscall.EINTR) || e == Errno(syscall.EMFILE) || e.Timeout() } func (e Errno) Timeout() bool { diff --git a/src/pkg/os/exec_posix.go b/src/pkg/os/exec_posix.go index 9102dc0a4..bf992ef42 100644 --- a/src/pkg/os/exec_posix.go +++ b/src/pkg/os/exec_posix.go @@ -4,7 +4,25 @@ package os -import "syscall" +import ( + "runtime" + "syscall" +) + +// A Signal can represent any operating system signal. +type Signal interface { + String() string +} + +type UnixSignal int32 + +func (sig UnixSignal) String() string { + s := runtime.Signame(int32(sig)) + if len(s) > 0 { + return s + } + return "UnixSignal" +} // StartProcess starts a new process with the program, arguments and attributes // specified by name, argv and attr. @@ -34,6 +52,11 @@ func StartProcess(name string, argv []string, attr *ProcAttr) (p *Process, err E return newProcess(pid, h), nil } +// Kill causes the Process to exit immediately. +func (p *Process) Kill() Error { + return p.Signal(SIGKILL) +} + // Exec replaces the current process with an execution of the // named binary, with arguments argv and environment envv. // If successful, Exec never returns. If it fails, it returns an Error. diff --git a/src/pkg/os/exec_unix.go b/src/pkg/os/exec_unix.go index 8990d6a97..cf5ea9b61 100644 --- a/src/pkg/os/exec_unix.go +++ b/src/pkg/os/exec_unix.go @@ -45,6 +45,14 @@ func (p *Process) Wait(options int) (w *Waitmsg, err Error) { return w, nil } +// Signal sends a signal to the Process. +func (p *Process) Signal(sig Signal) Error { + if e := syscall.Kill(p.Pid, int(sig.(UnixSignal))); e != 0 { + return Errno(e) + } + return nil +} + // Release releases any resources associated with the Process. func (p *Process) Release() Error { // NOOP for unix. diff --git a/src/pkg/os/exec_windows.go b/src/pkg/os/exec_windows.go index ae8ffeab2..bac33b908 100644 --- a/src/pkg/os/exec_windows.go +++ b/src/pkg/os/exec_windows.go @@ -20,13 +20,23 @@ func (p *Process) Wait(options int) (w *Waitmsg, err Error) { return nil, ErrorString("os: unexpected result from WaitForSingleObject") } var ec uint32 - e = syscall.GetExitCodeProcess(uint32(p.handle), &ec) + e = syscall.GetExitCodeProcess(int32(p.handle), &ec) if e != 0 { return nil, NewSyscallError("GetExitCodeProcess", e) } return &Waitmsg{p.Pid, syscall.WaitStatus{s, ec}, new(syscall.Rusage)}, nil } +// Signal sends a signal to the Process. +func (p *Process) Signal(sig Signal) Error { + switch sig.(UnixSignal) { + case SIGKILL: + e := syscall.TerminateProcess(int32(p.handle), 1) + return NewSyscallError("TerminateProcess", e) + } + return Errno(syscall.EWINDOWS) +} + func (p *Process) Release() Error { if p.handle == -1 { return EINVAL diff --git a/src/pkg/os/signal/mkunix.sh b/src/pkg/os/mkunixsignals.sh index 653b01664..6ec764cbd 100755 --- a/src/pkg/os/signal/mkunix.sh +++ b/src/pkg/os/mkunixsignals.sh @@ -8,7 +8,7 @@ echo '// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT' echo cat <<EOH -package signal +package os import ( "syscall" diff --git a/src/pkg/os/signal/Makefile b/src/pkg/os/signal/Makefile index 013b91a85..26f58760e 100644 --- a/src/pkg/os/signal/Makefile +++ b/src/pkg/os/signal/Makefile @@ -7,11 +7,5 @@ include ../../../Make.inc TARG=os/signal GOFILES=\ signal.go\ - unix.go\ - -CLEANFILES+=unix.go include ../../../Make.pkg - -unix.go: ../../syscall/zerrors_$(GOOS)_$(GOARCH).go - ./mkunix.sh $< > $@ || rm -f $@ diff --git a/src/pkg/os/signal/signal.go b/src/pkg/os/signal/signal.go index 666c03e73..520f3f8a9 100644 --- a/src/pkg/os/signal/signal.go +++ b/src/pkg/os/signal/signal.go @@ -6,35 +6,20 @@ package signal import ( + "os" "runtime" - "strconv" ) -// A Signal can represent any operating system signal. -type Signal interface { - String() string -} - -type UnixSignal int32 - -func (sig UnixSignal) String() string { - s := runtime.Signame(int32(sig)) - if len(s) > 0 { - return s - } - return "Signal " + strconv.Itoa(int(sig)) -} - // Incoming is the global signal channel. // All signals received by the program will be delivered to this channel. -var Incoming <-chan Signal +var Incoming <-chan os.Signal -func process(ch chan<- Signal) { +func process(ch chan<- os.Signal) { for { var mask uint32 = runtime.Sigrecv() for sig := uint(0); sig < 32; sig++ { if mask&(1<<sig) != 0 { - ch <- UnixSignal(sig) + ch <- os.UnixSignal(sig) } } } @@ -42,7 +27,7 @@ func process(ch chan<- Signal) { func init() { runtime.Siginit() - ch := make(chan Signal) // Done here so Incoming can have type <-chan Signal + ch := make(chan os.Signal) // Done here so Incoming can have type <-chan Signal Incoming = ch go process(ch) } diff --git a/src/pkg/os/signal/signal_test.go b/src/pkg/os/signal/signal_test.go index f2679f14d..00eb29578 100644 --- a/src/pkg/os/signal/signal_test.go +++ b/src/pkg/os/signal/signal_test.go @@ -5,6 +5,7 @@ package signal import ( + "os" "syscall" "testing" ) @@ -13,7 +14,7 @@ func TestSignal(t *testing.T) { // Send this process a SIGHUP. syscall.Syscall(syscall.SYS_KILL, uintptr(syscall.Getpid()), syscall.SIGHUP, 0) - if sig := (<-Incoming).(UnixSignal); sig != SIGHUP { - t.Errorf("signal was %v, want %v", sig, SIGHUP) + if sig := (<-Incoming).(os.UnixSignal); sig != os.SIGHUP { + t.Errorf("signal was %v, want %v", sig, os.SIGHUP) } } diff --git a/src/pkg/runtime/386/memmove.s b/src/pkg/runtime/386/memmove.s index 38a0652b5..471553ba2 100644 --- a/src/pkg/runtime/386/memmove.s +++ b/src/pkg/runtime/386/memmove.s @@ -32,7 +32,6 @@ TEXT runtime·memmove(SB), 7, $0 /* * check and set for backwards - * should we look closer for overlap? */ CMPL SI, DI JLS back @@ -40,6 +39,7 @@ TEXT runtime·memmove(SB), 7, $0 /* * forward copy loop */ +forward: MOVL BX, CX SHRL $2, CX ANDL $3, BX @@ -51,10 +51,18 @@ TEXT runtime·memmove(SB), 7, $0 MOVL to+0(FP),AX RET /* + * check overlap + */ +back: + MOVL SI, CX + ADDL BX, CX + CMPL CX, DI + JLS forward +/* * whole thing backwards has * adjusted addresses */ -back: + ADDL BX, DI ADDL BX, SI STD diff --git a/src/pkg/runtime/amd64/memmove.s b/src/pkg/runtime/amd64/memmove.s index 9966b0ba7..fc9573f72 100644 --- a/src/pkg/runtime/amd64/memmove.s +++ b/src/pkg/runtime/amd64/memmove.s @@ -33,7 +33,6 @@ TEXT runtime·memmove(SB), 7, $0 /* * check and set for backwards - * should we look closer for overlap? */ CMPQ SI, DI JLS back @@ -41,6 +40,7 @@ TEXT runtime·memmove(SB), 7, $0 /* * forward copy loop */ +forward: MOVQ BX, CX SHRQ $3, CX ANDQ $7, BX @@ -51,11 +51,19 @@ TEXT runtime·memmove(SB), 7, $0 MOVQ to+0(FP),AX RET +back: +/* + * check overlap + */ + MOVQ SI, CX + ADDQ BX, CX + CMPQ CX, DI + JLS forward + /* * whole thing backwards has * adjusted addresses */ -back: ADDQ BX, DI ADDQ BX, SI STD diff --git a/src/pkg/runtime/arm/softfloat.c b/src/pkg/runtime/arm/softfloat.c index f91a6fc09..0a071dada 100644 --- a/src/pkg/runtime/arm/softfloat.c +++ b/src/pkg/runtime/arm/softfloat.c @@ -15,6 +15,7 @@ #define FLAGS_V (1 << 28) void runtime·abort(void); +void math·sqrtGoC(uint64, uint64*); static uint32 trace = 0; @@ -357,6 +358,15 @@ stage3: // regd, regm are 4bit variables regd, regm, m->freghi[regd], m->freglo[regd]); break; + case 0xeeb10bc0: // D[regd] = sqrt D[regm] + math·sqrtGoC(getd(regm), &uval); + putd(regd, uval); + + if(trace) + runtime·printf("*** D[%d] = sqrt D[%d] %x-%x\n", + regd, regm, m->freghi[regd], m->freglo[regd]); + break; + case 0xeeb40bc0: // D[regd] :: D[regm] (CMPD) runtime·fcmp64c(getd(regd), getd(regm), &cmp, &nan); m->fflag = fstatus(nan, cmp); diff --git a/src/pkg/runtime/linux/386/sys.s b/src/pkg/runtime/linux/386/sys.s index 868a0d901..e8b423324 100644 --- a/src/pkg/runtime/linux/386/sys.s +++ b/src/pkg/runtime/linux/386/sys.s @@ -47,6 +47,14 @@ TEXT runtime·setitimer(SB),7,$0-24 INT $0x80 RET +TEXT runtime·mincore(SB),7,$0-24 + MOVL $218, AX // syscall - mincore + MOVL 4(SP), BX + MOVL 8(SP), CX + MOVL 12(SP), DX + INT $0x80 + RET + TEXT runtime·gettime(SB), 7, $32 MOVL $78, AX // syscall - gettimeofday LEAL 8(SP), BX diff --git a/src/pkg/runtime/linux/amd64/sys.s b/src/pkg/runtime/linux/amd64/sys.s index eadd30005..66fdab208 100644 --- a/src/pkg/runtime/linux/amd64/sys.s +++ b/src/pkg/runtime/linux/amd64/sys.s @@ -53,6 +53,14 @@ TEXT runtime·setitimer(SB),7,$0-24 SYSCALL RET +TEXT runtime·mincore(SB),7,$0-24 + MOVQ 8(SP), DI + MOVQ 16(SP), SI + MOVQ 24(SP), DX + MOVL $27, AX // syscall entry + SYSCALL + RET + TEXT runtime·gettime(SB), 7, $32 LEAQ 8(SP), DI MOVQ $0, SI diff --git a/src/pkg/runtime/linux/arm/sys.s b/src/pkg/runtime/linux/arm/sys.s index 2b5365bd8..ab5349822 100644 --- a/src/pkg/runtime/linux/arm/sys.s +++ b/src/pkg/runtime/linux/arm/sys.s @@ -26,6 +26,7 @@ #define SYS_exit_group (SYS_BASE + 248) #define SYS_munmap (SYS_BASE + 91) #define SYS_setitimer (SYS_BASE + 104) +#define SYS_mincore (SYS_BASE + 219) #define SYS_gettid (SYS_BASE + 224) #define SYS_tkill (SYS_BASE + 238) @@ -91,6 +92,14 @@ TEXT runtime·setitimer(SB),7,$0 SWI $0 RET +TEXT runtime·mincore(SB),7,$0 + MOVW 0(FP), R0 + MOVW 4(FP), R1 + MOVW 8(FP), R2 + MOVW $SYS_mincore, R7 + SWI $0 + RET + TEXT runtime·gettime(SB),7,$32 /* dummy version - return 0,0 */ MOVW $0, R1 diff --git a/src/pkg/runtime/linux/mem.c b/src/pkg/runtime/linux/mem.c index 02f798732..38ca7e4a0 100644 --- a/src/pkg/runtime/linux/mem.c +++ b/src/pkg/runtime/linux/mem.c @@ -3,6 +3,30 @@ #include "os.h" #include "malloc.h" +enum +{ + ENOMEM = 12, +}; + +static int32 +addrspace_free(void *v, uintptr n) +{ + uintptr page_size = 4096; + uintptr off; + int8 one_byte; + + for(off = 0; off < n; off += page_size) { + int32 errval = runtime·mincore((int8 *)v + off, page_size, (void *)&one_byte); + // errval is 0 if success, or -(error_code) if error. + if (errval == 0 || errval != -ENOMEM) + return 0; + } + USED(v); + USED(n); + return 1; +} + + void* runtime·SysAlloc(uintptr n) { @@ -54,11 +78,6 @@ runtime·SysReserve(void *v, uintptr n) return p; } -enum -{ - ENOMEM = 12, -}; - void runtime·SysMap(void *v, uintptr n) { @@ -69,6 +88,11 @@ runtime·SysMap(void *v, uintptr n) // On 64-bit, we don't actually have v reserved, so tread carefully. if(sizeof(void*) == 8) { p = runtime·mmap(v, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_PRIVATE, -1, 0); + if(p != v && addrspace_free(v, n)) { + // On some systems, mmap ignores v without + // MAP_FIXED, so retry if the address space is free. + p = runtime·mmap(v, n, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0); + } if(p == (void*)ENOMEM) runtime·throw("runtime: out of memory"); if(p != v) { diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h index 2b2b34a3c..f3ccff1bc 100644 --- a/src/pkg/runtime/runtime.h +++ b/src/pkg/runtime/runtime.h @@ -413,6 +413,7 @@ int32 runtime·gotraceback(void); void runtime·traceback(uint8 *pc, uint8 *sp, uint8 *lr, G* gp); void runtime·tracebackothers(G*); int32 runtime·write(int32, void*, int32); +int32 runtime·mincore(void*, uintptr, byte*); bool runtime·cas(uint32*, uint32, uint32); bool runtime·casp(void**, void*, void*); uint32 runtime·xadd(uint32 volatile*, int32); diff --git a/src/pkg/runtime/windows/thread.c b/src/pkg/runtime/windows/thread.c index 2ce8fae15..81ad68033 100644 --- a/src/pkg/runtime/windows/thread.c +++ b/src/pkg/runtime/windows/thread.c @@ -373,7 +373,7 @@ runtime·compilecallback(Eface fn, bool cleanstack) return &c->asmbody; } } - if(cbs.n >= 20) + if(cbs.n >= 2000) runtime·throw("too many callback functions"); c = runtime·mal(sizeof *c + n); c->gobody = fn.data; diff --git a/src/pkg/strconv/quote.go b/src/pkg/strconv/quote.go index bbc0b2658..98b19d3a2 100644 --- a/src/pkg/strconv/quote.go +++ b/src/pkg/strconv/quote.go @@ -14,56 +14,68 @@ import ( const lowerhex = "0123456789abcdef" -func quoteWith(s string, quote byte) string { +func quoteWith(s string, quote byte, ASCIIonly bool) string { var buf bytes.Buffer buf.WriteByte(quote) - for ; len(s) > 0; s = s[1:] { - switch c := s[0]; { - case c == quote: + for width := 0; len(s) > 0; s = s[width:] { + rune := int(s[0]) + width = 1 + if rune >= utf8.RuneSelf { + rune, width = utf8.DecodeRuneInString(s) + } + if width == 1 && rune == utf8.RuneError { + goto printEscX + } + if rune == int(quote) || rune == '\\' { // always backslashed buf.WriteByte('\\') - buf.WriteByte(quote) - case c == '\\': - buf.WriteString(`\\`) - case ' ' <= c && c <= '~': - buf.WriteString(string(c)) - case c == '\a': + buf.WriteByte(byte(rune)) + continue + } + if ASCIIonly { + if rune <= unicode.MaxASCII && unicode.IsPrint(rune) { + buf.WriteRune(rune) + continue + } + } else if unicode.IsPrint(rune) { + buf.WriteRune(rune) + continue + } + switch rune { + case '\a': buf.WriteString(`\a`) - case c == '\b': + case '\b': buf.WriteString(`\b`) - case c == '\f': + case '\f': buf.WriteString(`\f`) - case c == '\n': + case '\n': buf.WriteString(`\n`) - case c == '\r': + case '\r': buf.WriteString(`\r`) - case c == '\t': + case '\t': buf.WriteString(`\t`) - case c == '\v': + case '\v': buf.WriteString(`\v`) - - case c >= utf8.RuneSelf && utf8.FullRuneInString(s): - r, size := utf8.DecodeRuneInString(s) - if r == utf8.RuneError && size == 1 { - goto EscX - } - s = s[size-1:] // next iteration will slice off 1 more - if r < 0x10000 { + default: + switch { + case rune < ' ': + printEscX: + buf.WriteString(`\x`) + buf.WriteByte(lowerhex[s[0]>>4]) + buf.WriteByte(lowerhex[s[0]&0xF]) + case rune > unicode.MaxRune: + rune = 0xFFFD + fallthrough + case rune < 0x10000: buf.WriteString(`\u`) - for j := uint(0); j < 4; j++ { - buf.WriteByte(lowerhex[(r>>(12-4*j))&0xF]) + for s := 12; s >= 0; s -= 4 { + buf.WriteByte(lowerhex[rune>>uint(s)&0xF]) } - } else { + default: buf.WriteString(`\U`) - for j := uint(0); j < 8; j++ { - buf.WriteByte(lowerhex[(r>>(28-4*j))&0xF]) + for s := 28; s >= 0; s -= 4 { + buf.WriteByte(lowerhex[rune>>uint(s)&0xF]) } } - - default: - EscX: - buf.WriteString(`\x`) - buf.WriteByte(lowerhex[c>>4]) - buf.WriteByte(lowerhex[c&0xF]) } } buf.WriteByte(quote) @@ -71,21 +83,38 @@ func quoteWith(s string, quote byte) string { } -// Quote returns a double-quoted Go string literal -// representing s. The returned string uses Go escape -// sequences (\t, \n, \xFF, \u0100) for control characters -// and non-ASCII characters. +// Quote returns a double-quoted Go string literal representing s. The +// returned string uses Go escape sequences (\t, \n, \xFF, \u0100) for +// control characters and non-printable characters as defined by +// unicode.IsPrint. func Quote(s string) string { - return quoteWith(s, '"') + return quoteWith(s, '"', false) +} + +// QuoteToASCII returns a double-quoted Go string literal representing s. +// The returned string uses Go escape sequences (\t, \n, \xFF, \u0100) for +// non-ASCII characters and non-printable characters as defined by +// unicode.IsPrint. +func QuoteToASCII(s string) string { + return quoteWith(s, '"', true) } -// QuoteRune returns a single-quoted Go character literal -// representing the rune. The returned string uses Go escape -// sequences (\t, \n, \xFF, \u0100) for control characters -// and non-ASCII characters. +// QuoteRune returns a single-quoted Go character literal representing the +// rune. The returned string uses Go escape sequences (\t, \n, \xFF, \u0100) +// for control characters and non-printable characters as defined by +// unicode.IsPrint. func QuoteRune(rune int) string { // TODO: avoid the allocation here. - return quoteWith(string(rune), '\'') + return quoteWith(string(rune), '\'', false) +} + +// QuoteRuneToASCII returns a single-quoted Go character literal representing +// the rune. The returned string uses Go escape sequences (\t, \n, \xFF, +// \u0100) for non-ASCII characters and non-printable characters as defined +// by unicode.IsPrint. +func QuoteRuneToASCII(rune int) string { + // TODO: avoid the allocation here. + return quoteWith(string(rune), '\'', true) } // CanBackquote returns whether the string s would be diff --git a/src/pkg/strconv/quote_test.go b/src/pkg/strconv/quote_test.go index 3232d611c..4d615db44 100644 --- a/src/pkg/strconv/quote_test.go +++ b/src/pkg/strconv/quote_test.go @@ -11,17 +11,18 @@ import ( ) type quoteTest struct { - in string - out string + in string + out string + ascii string } var quotetests = []quoteTest{ - {"\a\b\f\r\n\t\v", `"\a\b\f\r\n\t\v"`}, - {"\\", `"\\"`}, - {"abc\xffdef", `"abc\xffdef"`}, - {"\u263a", `"\u263a"`}, - {"\U0010ffff", `"\U0010ffff"`}, - {"\x04", `"\x04"`}, + {"\a\b\f\r\n\t\v", `"\a\b\f\r\n\t\v"`, `"\a\b\f\r\n\t\v"`}, + {"\\", `"\\"`, `"\\"`}, + {"abc\xffdef", `"abc\xffdef"`, `"abc\xffdef"`}, + {"\u263a", `"☺"`, `"\u263a"`}, + {"\U0010ffff", `"\U0010ffff"`, `"\U0010ffff"`}, + {"\x04", `"\x04"`, `"\x04"`}, } func TestQuote(t *testing.T) { @@ -32,20 +33,30 @@ func TestQuote(t *testing.T) { } } +func TestQuoteToASCII(t *testing.T) { + for _, tt := range quotetests { + if out := QuoteToASCII(tt.in); out != tt.ascii { + t.Errorf("QuoteToASCII(%s) = %s, want %s", tt.in, out, tt.ascii) + } + } +} + type quoteRuneTest struct { - in int - out string + in int + out string + ascii string } var quoterunetests = []quoteRuneTest{ - {'a', `'a'`}, - {'\a', `'\a'`}, - {'\\', `'\\'`}, - {0xFF, `'\u00ff'`}, - {0x263a, `'\u263a'`}, - {0x0010ffff, `'\U0010ffff'`}, - {0x0010ffff + 1, `'\ufffd'`}, - {0x04, `'\x04'`}, + {'a', `'a'`, `'a'`}, + {'\a', `'\a'`, `'\a'`}, + {'\\', `'\\'`, `'\\'`}, + {0xFF, `'ÿ'`, `'\u00ff'`}, + {0x263a, `'☺'`, `'\u263a'`}, + {0xfffd, `'�'`, `'\ufffd'`}, + {0x0010ffff, `'\U0010ffff'`, `'\U0010ffff'`}, + {0x0010ffff + 1, `'�'`, `'\ufffd'`}, + {0x04, `'\x04'`, `'\x04'`}, } func TestQuoteRune(t *testing.T) { @@ -56,6 +67,14 @@ func TestQuoteRune(t *testing.T) { } } +func TestQuoteRuneToASCII(t *testing.T) { + for _, tt := range quoterunetests { + if out := QuoteRuneToASCII(tt.in); out != tt.ascii { + t.Errorf("QuoteRuneToASCII(%U) = %s, want %s", tt.in, out, tt.ascii) + } + } +} + type canBackquoteTest struct { in string out bool @@ -110,7 +129,12 @@ func TestCanBackquote(t *testing.T) { } } -var unquotetests = []quoteTest{ +type unQuoteTest struct { + in string + out string +} + +var unquotetests = []unQuoteTest{ {`""`, ""}, {`"a"`, "a"}, {`"abc"`, "abc"}, diff --git a/src/pkg/sync/atomic/asm_arm.s b/src/pkg/sync/atomic/asm_arm.s index 448a98a01..95e2f5be4 100644 --- a/src/pkg/sync/atomic/asm_arm.s +++ b/src/pkg/sync/atomic/asm_arm.s @@ -90,11 +90,11 @@ add64loop: TEXT check64<>(SB),7,$16 MOVW $10, R1 // 8-aligned stack address scratch space. - MOVW $8(SP), R3 - AND $~7, R3 + MOVW $8(R13), R5 + AND $~7, R5 loop: - LDREXD (R3), R2 - STREXD R2, (R3), R0 + LDREXD (R5), R2 + STREXD R2, (R5), R0 CMP $0, R0 BEQ ok SUB $1, R1 diff --git a/src/pkg/sync/rwmutex_test.go b/src/pkg/sync/rwmutex_test.go index 9fb89f8e8..0480a6601 100644 --- a/src/pkg/sync/rwmutex_test.go +++ b/src/pkg/sync/rwmutex_test.go @@ -45,6 +45,7 @@ func doTestParallelReaders(numReaders, gomaxprocs int) { } func TestParallelReaders(t *testing.T) { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1)) doTestParallelReaders(1, 4) doTestParallelReaders(3, 4) doTestParallelReaders(4, 2) @@ -102,6 +103,7 @@ func HammerRWMutex(gomaxprocs, numReaders, num_iterations int) { } func TestRWMutex(t *testing.T) { + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(-1)) n := 1000 if testing.Short() { n = 5 diff --git a/src/pkg/syscall/syscall_windows.go b/src/pkg/syscall/syscall_windows.go index bb93533bd..d01664d12 100644 --- a/src/pkg/syscall/syscall_windows.go +++ b/src/pkg/syscall/syscall_windows.go @@ -141,8 +141,9 @@ func Sendfile(outfd int, infd int, offset *int64, count int) (written int, errno //sys GetQueuedCompletionStatus(cphandle int32, qty *uint32, key *uint32, overlapped **Overlapped, timeout uint32) (errno int) //sys CancelIo(s uint32) (errno int) //sys CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityAttributes, threadSecurity *SecurityAttributes, inheritHandles bool, creationFlags uint32, env *uint16, currentDir *uint16, startupInfo *StartupInfo, outProcInfo *ProcessInformation) (errno int) = CreateProcessW -//sys OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle uint32, errno int) -//sys GetExitCodeProcess(handle uint32, exitcode *uint32) (errno int) +//sys OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle int32, errno int) +//sys TerminateProcess(handle int32, exitcode uint32) (errno int) +//sys GetExitCodeProcess(handle int32, exitcode *uint32) (errno int) //sys GetStartupInfo(startupInfo *StartupInfo) (errno int) = GetStartupInfoW //sys GetCurrentProcess() (pseudoHandle int32, errno int) //sys DuplicateHandle(hSourceProcessHandle int32, hSourceHandle int32, hTargetProcessHandle int32, lpTargetHandle *int32, dwDesiredAccess uint32, bInheritHandle bool, dwOptions uint32) (errno int) @@ -697,10 +698,6 @@ func BindToDevice(fd int, device string) (errno int) { return // TODO(brainman): fix all needed for os -const ( - SIGTRAP = 5 -) - func Getpid() (pid int) { return -1 } func Getppid() (ppid int) { return -1 } diff --git a/src/pkg/syscall/zsyscall_windows_386.go b/src/pkg/syscall/zsyscall_windows_386.go index ce36ab6c0..447b09043 100644 --- a/src/pkg/syscall/zsyscall_windows_386.go +++ b/src/pkg/syscall/zsyscall_windows_386.go @@ -46,6 +46,7 @@ var ( procCancelIo = getSysProcAddr(modkernel32, "CancelIo") procCreateProcessW = getSysProcAddr(modkernel32, "CreateProcessW") procOpenProcess = getSysProcAddr(modkernel32, "OpenProcess") + procTerminateProcess = getSysProcAddr(modkernel32, "TerminateProcess") procGetExitCodeProcess = getSysProcAddr(modkernel32, "GetExitCodeProcess") procGetStartupInfoW = getSysProcAddr(modkernel32, "GetStartupInfoW") procGetCurrentProcess = getSysProcAddr(modkernel32, "GetCurrentProcess") @@ -542,7 +543,7 @@ func CreateProcess(appName *uint16, commandLine *uint16, procSecurity *SecurityA return } -func OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle uint32, errno int) { +func OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle int32, errno int) { var _p0 uint32 if inheritHandle { _p0 = 1 @@ -550,7 +551,7 @@ func OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle uint32, errn _p0 = 0 } r0, _, e1 := Syscall(procOpenProcess, 3, uintptr(da), uintptr(_p0), uintptr(pid)) - handle = uint32(r0) + handle = int32(r0) if handle == 0 { if e1 != 0 { errno = int(e1) @@ -563,7 +564,21 @@ func OpenProcess(da uint32, inheritHandle bool, pid uint32) (handle uint32, errn return } -func GetExitCodeProcess(handle uint32, exitcode *uint32) (errno int) { +func TerminateProcess(handle int32, exitcode uint32) (errno int) { + r1, _, e1 := Syscall(procTerminateProcess, 2, uintptr(handle), uintptr(exitcode), 0) + if int(r1) == 0 { + if e1 != 0 { + errno = int(e1) + } else { + errno = EINVAL + } + } else { + errno = 0 + } + return +} + +func GetExitCodeProcess(handle int32, exitcode *uint32) (errno int) { r1, _, e1 := Syscall(procGetExitCodeProcess, 2, uintptr(handle), uintptr(unsafe.Pointer(exitcode)), 0) if int(r1) == 0 { if e1 != 0 { diff --git a/src/pkg/syscall/ztypes_darwin_386.go b/src/pkg/syscall/ztypes_darwin_386.go index 2dec01787..ba6e590c4 100644 --- a/src/pkg/syscall/ztypes_darwin_386.go +++ b/src/pkg/syscall/ztypes_darwin_386.go @@ -2,10 +2,6 @@ // MACHINE GENERATED - DO NOT EDIT. -// Manual corrections: TODO: need to fix godefs (issue 1466) -// change Msghdr field to Iov *Iovec (was uint32/64) -// change BpfProgram field to Insns *BpfInsn (was uint32/64) - package syscall // Constants diff --git a/src/pkg/syscall/ztypes_darwin_amd64.go b/src/pkg/syscall/ztypes_darwin_amd64.go index 96500d732..59c832812 100644 --- a/src/pkg/syscall/ztypes_darwin_amd64.go +++ b/src/pkg/syscall/ztypes_darwin_amd64.go @@ -2,10 +2,6 @@ // MACHINE GENERATED - DO NOT EDIT. -// Manual corrections: TODO: need to fix godefs (issue 1466) -// change Msghdr field to Iov *Iovec (was uint32/64) -// change BpfProgram field to Insns *BpfInsn (was uint32/64) - package syscall // Constants diff --git a/src/pkg/syscall/ztypes_windows_386.go b/src/pkg/syscall/ztypes_windows_386.go index 7b15ea404..b04fea576 100644 --- a/src/pkg/syscall/ztypes_windows_386.go +++ b/src/pkg/syscall/ztypes_windows_386.go @@ -49,6 +49,23 @@ const ( ) const ( + // More invented values for signals + SIGHUP = 0x1 + SIGINT = 0x2 + SIGQUIT = 0x3 + SIGILL = 0x4 + SIGTRAP = 0x5 + SIGABRT = 0x6 + SIGBUS = 0x7 + SIGFPE = 0x8 + SIGKILL = 0x9 + SIGSEGV = 0xb + SIGPIPE = 0xd + SIGALRM = 0xe + SIGTERM = 0xf +) + +const ( GENERIC_READ = 0x80000000 GENERIC_WRITE = 0x40000000 GENERIC_EXECUTE = 0x20000000 diff --git a/src/pkg/template/Makefile b/src/pkg/template/Makefile index 4915527b4..4f1e06527 100644 --- a/src/pkg/template/Makefile +++ b/src/pkg/template/Makefile @@ -6,7 +6,9 @@ include ../../Make.inc TARG=template GOFILES=\ + doc.go\ + execute.go\ format.go\ - template.go\ + parse.go\ include ../../Make.pkg diff --git a/src/pkg/template/doc.go b/src/pkg/template/doc.go new file mode 100644 index 000000000..e778d801d --- /dev/null +++ b/src/pkg/template/doc.go @@ -0,0 +1,91 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* + Package template implements data-driven templates for generating textual + output such as HTML. + + Templates are executed by applying them to a data structure. + Annotations in the template refer to elements of the data + structure (typically a field of a struct or a key in a map) + to control execution and derive values to be displayed. + The template walks the structure as it executes and the + "cursor" @ represents the value at the current location + in the structure. + + Data items may be values or pointers; the interface hides the + indirection. + + In the following, 'Field' is one of several things, according to the data. + + - The name of a field of a struct (result = data.Field), + - The value stored in a map under that key (result = data["Field"]), or + - The result of invoking a niladic single-valued method with that name + (result = data.Field()) + + If Field is a struct field or method name, it must be an exported + (capitalized) name. + + Major constructs ({} are the default delimiters for template actions; + [] are the notation in this comment for optional elements): + + {# comment } + + A one-line comment. + + {.section field} XXX [ {.or} YYY ] {.end} + + Set @ to the value of the field. It may be an explicit @ + to stay at the same point in the data. If the field is nil + or empty, execute YYY; otherwise execute XXX. + + {.repeated section field} XXX [ {.alternates with} ZZZ ] [ {.or} YYY ] {.end} + + Like .section, but field must be an array or slice. XXX + is executed for each element. If the array is nil or empty, + YYY is executed instead. If the {.alternates with} marker + is present, ZZZ is executed between iterations of XXX. + + {field} + {field1 field2 ...} + {field|formatter} + {field1 field2...|formatter} + {field|formatter1|formatter2} + + Insert the value of the fields into the output. Each field is + first looked for in the cursor, as in .section and .repeated. + If it is not found, the search continues in outer sections + until the top level is reached. + + If the field value is a pointer, leading asterisks indicate + that the value to be inserted should be evaluated through the + pointer. For example, if x.p is of type *int, {x.p} will + insert the value of the pointer but {*x.p} will insert the + value of the underlying integer. If the value is nil or not a + pointer, asterisks have no effect. + + If a formatter is specified, it must be named in the formatter + map passed to the template set up routines or in the default + set ("html","str","") and is used to process the data for + output. The formatter function has signature + func(wr io.Writer, formatter string, data ...interface{}) + where wr is the destination for output, data holds the field + values at the instantiation, and formatter is its name at + the invocation site. The default formatter just concatenates + the string representations of the fields. + + Multiple formatters separated by the pipeline character | are + executed sequentially, with each formatter receiving the bytes + emitted by the one to its left. + + As well as field names, one may use literals with Go syntax. + Integer, floating-point, and string literals are supported. + Raw strings may not span newlines. + + The delimiter strings get their default value, "{" and "}", from + JSON-template. They may be set to any non-empty, space-free + string using the SetDelims method. Their value can be printed + in the output using {.meta-left} and {.meta-right}. +*/ +package template diff --git a/src/pkg/template/execute.go b/src/pkg/template/execute.go new file mode 100644 index 000000000..5bc7ff7e9 --- /dev/null +++ b/src/pkg/template/execute.go @@ -0,0 +1,346 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Code to execute a parsed template. + +package template + +import ( + "bytes" + "io" + "reflect" + "strings" +) + +// Internal state for executing a Template. As we evaluate the struct, +// the data item descends into the fields associated with sections, etc. +// Parent is used to walk upwards to find variables higher in the tree. +type state struct { + parent *state // parent in hierarchy + data reflect.Value // the driver data for this section etc. + wr io.Writer // where to send output + buf [2]bytes.Buffer // alternating buffers used when chaining formatters +} + +func (parent *state) clone(data reflect.Value) *state { + return &state{parent: parent, data: data, wr: parent.wr} +} + +// Evaluate interfaces and pointers looking for a value that can look up the name, via a +// struct field, method, or map key, and return the result of the lookup. +func (t *Template) lookup(st *state, v reflect.Value, name string) reflect.Value { + for v.IsValid() { + typ := v.Type() + if n := v.Type().NumMethod(); n > 0 { + for i := 0; i < n; i++ { + m := typ.Method(i) + mtyp := m.Type + if m.Name == name && mtyp.NumIn() == 1 && mtyp.NumOut() == 1 { + if !isExported(name) { + t.execError(st, t.linenum, "name not exported: %s in type %s", name, st.data.Type()) + } + return v.Method(i).Call(nil)[0] + } + } + } + switch av := v; av.Kind() { + case reflect.Ptr: + v = av.Elem() + case reflect.Interface: + v = av.Elem() + case reflect.Struct: + if !isExported(name) { + t.execError(st, t.linenum, "name not exported: %s in type %s", name, st.data.Type()) + } + return av.FieldByName(name) + case reflect.Map: + if v := av.MapIndex(reflect.ValueOf(name)); v.IsValid() { + return v + } + return reflect.Zero(typ.Elem()) + default: + return reflect.Value{} + } + } + return v +} + +// indirectPtr returns the item numLevels levels of indirection below the value. +// It is forgiving: if the value is not a pointer, it returns it rather than giving +// an error. If the pointer is nil, it is returned as is. +func indirectPtr(v reflect.Value, numLevels int) reflect.Value { + for i := numLevels; v.IsValid() && i > 0; i++ { + if p := v; p.Kind() == reflect.Ptr { + if p.IsNil() { + return v + } + v = p.Elem() + } else { + break + } + } + return v +} + +// Walk v through pointers and interfaces, extracting the elements within. +func indirect(v reflect.Value) reflect.Value { +loop: + for v.IsValid() { + switch av := v; av.Kind() { + case reflect.Ptr: + v = av.Elem() + case reflect.Interface: + v = av.Elem() + default: + break loop + } + } + return v +} + +// If the data for this template is a struct, find the named variable. +// Names of the form a.b.c are walked down the data tree. +// The special name "@" (the "cursor") denotes the current data. +// The value coming in (st.data) might need indirecting to reach +// a struct while the return value is not indirected - that is, +// it represents the actual named field. Leading stars indicate +// levels of indirection to be applied to the value. +func (t *Template) findVar(st *state, s string) reflect.Value { + data := st.data + flattenedName := strings.TrimLeft(s, "*") + numStars := len(s) - len(flattenedName) + s = flattenedName + if s == "@" { + return indirectPtr(data, numStars) + } + for _, elem := range strings.Split(s, ".", -1) { + // Look up field; data must be a struct or map. + data = t.lookup(st, data, elem) + if !data.IsValid() { + return reflect.Value{} + } + } + return indirectPtr(data, numStars) +} + +// Is there no data to look at? +func empty(v reflect.Value) bool { + v = indirect(v) + if !v.IsValid() { + return true + } + switch v.Kind() { + case reflect.Bool: + return v.Bool() == false + case reflect.String: + return v.String() == "" + case reflect.Struct: + return false + case reflect.Map: + return false + case reflect.Array: + return v.Len() == 0 + case reflect.Slice: + return v.Len() == 0 + } + return false +} + +// Look up a variable or method, up through the parent if necessary. +func (t *Template) varValue(name string, st *state) reflect.Value { + field := t.findVar(st, name) + if !field.IsValid() { + if st.parent == nil { + t.execError(st, t.linenum, "name not found: %s in type %s", name, st.data.Type()) + } + return t.varValue(name, st.parent) + } + return field +} + +func (t *Template) format(wr io.Writer, fmt string, val []interface{}, v *variableElement, st *state) { + fn := t.formatter(fmt) + if fn == nil { + t.execError(st, v.linenum, "missing formatter %s for variable", fmt) + } + fn(wr, fmt, val...) +} + +// Evaluate a variable, looking up through the parent if necessary. +// If it has a formatter attached ({var|formatter}) run that too. +func (t *Template) writeVariable(v *variableElement, st *state) { + // Resolve field names + val := make([]interface{}, len(v.args)) + for i, arg := range v.args { + if name, ok := arg.(fieldName); ok { + val[i] = t.varValue(string(name), st).Interface() + } else { + val[i] = arg + } + } + for i, fmt := range v.fmts[:len(v.fmts)-1] { + b := &st.buf[i&1] + b.Reset() + t.format(b, fmt, val, v, st) + val = val[0:1] + val[0] = b.Bytes() + } + t.format(st.wr, v.fmts[len(v.fmts)-1], val, v, st) +} + +// Execute element i. Return next index to execute. +func (t *Template) executeElement(i int, st *state) int { + switch elem := t.elems[i].(type) { + case *textElement: + st.wr.Write(elem.text) + return i + 1 + case *literalElement: + st.wr.Write(elem.text) + return i + 1 + case *variableElement: + t.writeVariable(elem, st) + return i + 1 + case *sectionElement: + t.executeSection(elem, st) + return elem.end + case *repeatedElement: + t.executeRepeated(elem, st) + return elem.end + } + e := t.elems[i] + t.execError(st, 0, "internal error: bad directive in execute: %v %T\n", reflect.ValueOf(e).Interface(), e) + return 0 +} + +// Execute the template. +func (t *Template) execute(start, end int, st *state) { + for i := start; i < end; { + i = t.executeElement(i, st) + } +} + +// Execute a .section +func (t *Template) executeSection(s *sectionElement, st *state) { + // Find driver data for this section. It must be in the current struct. + field := t.varValue(s.field, st) + if !field.IsValid() { + t.execError(st, s.linenum, ".section: cannot find field %s in %s", s.field, st.data.Type()) + } + st = st.clone(field) + start, end := s.start, s.or + if !empty(field) { + // Execute the normal block. + if end < 0 { + end = s.end + } + } else { + // Execute the .or block. If it's missing, do nothing. + start, end = s.or, s.end + if start < 0 { + return + } + } + for i := start; i < end; { + i = t.executeElement(i, st) + } +} + +// Return the result of calling the Iter method on v, or nil. +func iter(v reflect.Value) reflect.Value { + for j := 0; j < v.Type().NumMethod(); j++ { + mth := v.Type().Method(j) + fv := v.Method(j) + ft := fv.Type() + // TODO(rsc): NumIn() should return 0 here, because ft is from a curried FuncValue. + if mth.Name != "Iter" || ft.NumIn() != 1 || ft.NumOut() != 1 { + continue + } + ct := ft.Out(0) + if ct.Kind() != reflect.Chan || + ct.ChanDir()&reflect.RecvDir == 0 { + continue + } + return fv.Call(nil)[0] + } + return reflect.Value{} +} + +// Execute a .repeated section +func (t *Template) executeRepeated(r *repeatedElement, st *state) { + // Find driver data for this section. It must be in the current struct. + field := t.varValue(r.field, st) + if !field.IsValid() { + t.execError(st, r.linenum, ".repeated: cannot find field %s in %s", r.field, st.data.Type()) + } + field = indirect(field) + + start, end := r.start, r.or + if end < 0 { + end = r.end + } + if r.altstart >= 0 { + end = r.altstart + } + first := true + + // Code common to all the loops. + loopBody := func(newst *state) { + // .alternates between elements + if !first && r.altstart >= 0 { + for i := r.altstart; i < r.altend; { + i = t.executeElement(i, newst) + } + } + first = false + for i := start; i < end; { + i = t.executeElement(i, newst) + } + } + + if array := field; array.Kind() == reflect.Array || array.Kind() == reflect.Slice { + for j := 0; j < array.Len(); j++ { + loopBody(st.clone(array.Index(j))) + } + } else if m := field; m.Kind() == reflect.Map { + for _, key := range m.MapKeys() { + loopBody(st.clone(m.MapIndex(key))) + } + } else if ch := iter(field); ch.IsValid() { + for { + e, ok := ch.Recv() + if !ok { + break + } + loopBody(st.clone(e)) + } + } else { + t.execError(st, r.linenum, ".repeated: cannot repeat %s (type %s)", + r.field, field.Type()) + } + + if first { + // Empty. Execute the .or block, once. If it's missing, do nothing. + start, end := r.or, r.end + if start >= 0 { + newst := st.clone(field) + for i := start; i < end; { + i = t.executeElement(i, newst) + } + } + return + } +} + +// A valid delimiter must contain no space and be non-empty. +func validDelim(d []byte) bool { + if len(d) == 0 { + return false + } + for _, c := range d { + if isSpace(c) { + return false + } + } + return true +} diff --git a/src/pkg/template/template.go b/src/pkg/template/parse.go index f481cbd1e..b4aa5fcd2 100644 --- a/src/pkg/template/template.go +++ b/src/pkg/template/parse.go @@ -2,97 +2,11 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -/* - Package template implements data-driven templates for generating textual - output such as HTML. +// Code to parse a template. - Templates are executed by applying them to a data structure. - Annotations in the template refer to elements of the data - structure (typically a field of a struct or a key in a map) - to control execution and derive values to be displayed. - The template walks the structure as it executes and the - "cursor" @ represents the value at the current location - in the structure. - - Data items may be values or pointers; the interface hides the - indirection. - - In the following, 'Field' is one of several things, according to the data. - - - The name of a field of a struct (result = data.Field), - - The value stored in a map under that key (result = data["Field"]), or - - The result of invoking a niladic single-valued method with that name - (result = data.Field()) - - If Field is a struct field or method name, it must be an exported - (capitalized) name. - - Major constructs ({} are the default delimiters for template actions; - [] are the notation in this comment for optional elements): - - {# comment } - - A one-line comment. - - {.section field} XXX [ {.or} YYY ] {.end} - - Set @ to the value of the field. It may be an explicit @ - to stay at the same point in the data. If the field is nil - or empty, execute YYY; otherwise execute XXX. - - {.repeated section field} XXX [ {.alternates with} ZZZ ] [ {.or} YYY ] {.end} - - Like .section, but field must be an array or slice. XXX - is executed for each element. If the array is nil or empty, - YYY is executed instead. If the {.alternates with} marker - is present, ZZZ is executed between iterations of XXX. - - {field} - {field1 field2 ...} - {field|formatter} - {field1 field2...|formatter} - {field|formatter1|formatter2} - - Insert the value of the fields into the output. Each field is - first looked for in the cursor, as in .section and .repeated. - If it is not found, the search continues in outer sections - until the top level is reached. - - If the field value is a pointer, leading asterisks indicate - that the value to be inserted should be evaluated through the - pointer. For example, if x.p is of type *int, {x.p} will - insert the value of the pointer but {*x.p} will insert the - value of the underlying integer. If the value is nil or not a - pointer, asterisks have no effect. - - If a formatter is specified, it must be named in the formatter - map passed to the template set up routines or in the default - set ("html","str","") and is used to process the data for - output. The formatter function has signature - func(wr io.Writer, formatter string, data ...interface{}) - where wr is the destination for output, data holds the field - values at the instantiation, and formatter is its name at - the invocation site. The default formatter just concatenates - the string representations of the fields. - - Multiple formatters separated by the pipeline character | are - executed sequentially, with each formatter receiving the bytes - emitted by the one to its left. - - As well as field names, one may use literals with Go syntax. - Integer, floating-point, and string literals are supported. - Raw strings may not span newlines. - - The delimiter strings get their default value, "{" and "}", from - JSON-template. They may be set to any non-empty, space-free - string using the SetDelims method. Their value can be printed - in the output using {.meta-left} and {.meta-right}. -*/ package template import ( - "bytes" - "container/vector" "fmt" "io" "io/ioutil" @@ -113,6 +27,19 @@ type Error struct { func (e *Error) String() string { return fmt.Sprintf("line %d: %s", e.Line, e.Msg) } +// checkError is a deferred function to turn a panic with type *Error into a plain error return. +// Other panics are unexpected and so are re-enabled. +func checkError(error *os.Error) { + if v := recover(); v != nil { + if e, ok := v.(*Error); ok { + *error = e + } else { + // runtime errors should crash + panic(v) + } + } +} + // Most of the literals are aces. var lbrace = []byte{'{'} var rbrace = []byte{'}'} @@ -192,21 +119,7 @@ type Template struct { p int // position in buf linenum int // position in input // Parsed results: - elems *vector.Vector -} - -// Internal state for executing a Template. As we evaluate the struct, -// the data item descends into the fields associated with sections, etc. -// Parent is used to walk upwards to find variables higher in the tree. -type state struct { - parent *state // parent in hierarchy - data reflect.Value // the driver data for this section etc. - wr io.Writer // where to send output - buf [2]bytes.Buffer // alternating buffers used when chaining formatters -} - -func (parent *state) clone(data reflect.Value) *state { - return &state{parent: parent, data: data, wr: parent.wr} + elems []interface{} } // New creates a new template with the specified formatter map (which @@ -216,7 +129,7 @@ func New(fmap FormatterMap) *Template { t.fmap = fmap t.ldelim = lbrace t.rdelim = rbrace - t.elems = new(vector.Vector) + t.elems = make([]interface{}, 0, 16) return t } @@ -583,24 +496,24 @@ func (t *Template) parseSimple(item []byte) (done bool, tok int, w []string) { case tokComment: return case tokText: - t.elems.Push(&textElement{item}) + t.elems = append(t.elems, &textElement{item}) return case tokLiteral: switch w[0] { case ".meta-left": - t.elems.Push(&literalElement{t.ldelim}) + t.elems = append(t.elems, &literalElement{t.ldelim}) case ".meta-right": - t.elems.Push(&literalElement{t.rdelim}) + t.elems = append(t.elems, &literalElement{t.rdelim}) case ".space": - t.elems.Push(&literalElement{space}) + t.elems = append(t.elems, &literalElement{space}) case ".tab": - t.elems.Push(&literalElement{tab}) + t.elems = append(t.elems, &literalElement{tab}) default: t.parseError("internal error: unknown literal: %s", w[0]) } return case tokVariable: - t.elems.Push(t.newVariable(w)) + t.elems = append(t.elems, t.newVariable(w)) return } return false, tok, w @@ -610,11 +523,11 @@ func (t *Template) parseSimple(item []byte) (done bool, tok int, w []string) { func (t *Template) parseRepeated(words []string) *repeatedElement { r := new(repeatedElement) - t.elems.Push(r) + t.elems = append(t.elems, r) r.linenum = t.linenum r.field = words[2] // Scan section, collecting true and false (.or) blocks. - r.start = t.elems.Len() + r.start = len(t.elems) r.or = -1 r.altstart = -1 r.altend = -1 @@ -637,8 +550,8 @@ Loop: t.parseError("extra .or in .repeated section") break Loop } - r.altend = t.elems.Len() - r.or = t.elems.Len() + r.altend = len(t.elems) + r.or = len(t.elems) case tokSection: t.parseSection(w) case tokRepeated: @@ -652,26 +565,26 @@ Loop: t.parseError(".alternates inside .or block in .repeated section") break Loop } - r.altstart = t.elems.Len() + r.altstart = len(t.elems) default: t.parseError("internal error: unknown repeated section item: %s", item) break Loop } } if r.altend < 0 { - r.altend = t.elems.Len() + r.altend = len(t.elems) } - r.end = t.elems.Len() + r.end = len(t.elems) return r } func (t *Template) parseSection(words []string) *sectionElement { s := new(sectionElement) - t.elems.Push(s) + t.elems = append(t.elems, s) s.linenum = t.linenum s.field = words[1] // Scan section, collecting true and false (.or) blocks. - s.start = t.elems.Len() + s.start = len(t.elems) s.or = -1 Loop: for { @@ -692,7 +605,7 @@ Loop: t.parseError("extra .or in .section") break Loop } - s.or = t.elems.Len() + s.or = len(t.elems) case tokSection: t.parseSection(w) case tokRepeated: @@ -703,7 +616,7 @@ Loop: t.parseError("internal error: unknown section item: %s", item) } } - s.end = t.elems.Len() + s.end = len(t.elems) return s } @@ -732,337 +645,6 @@ func (t *Template) parse() { // -- Execution -// Evaluate interfaces and pointers looking for a value that can look up the name, via a -// struct field, method, or map key, and return the result of the lookup. -func (t *Template) lookup(st *state, v reflect.Value, name string) reflect.Value { - for v.IsValid() { - typ := v.Type() - if n := v.Type().NumMethod(); n > 0 { - for i := 0; i < n; i++ { - m := typ.Method(i) - mtyp := m.Type - if m.Name == name && mtyp.NumIn() == 1 && mtyp.NumOut() == 1 { - if !isExported(name) { - t.execError(st, t.linenum, "name not exported: %s in type %s", name, st.data.Type()) - } - return v.Method(i).Call(nil)[0] - } - } - } - switch av := v; av.Kind() { - case reflect.Ptr: - v = av.Elem() - case reflect.Interface: - v = av.Elem() - case reflect.Struct: - if !isExported(name) { - t.execError(st, t.linenum, "name not exported: %s in type %s", name, st.data.Type()) - } - return av.FieldByName(name) - case reflect.Map: - if v := av.MapIndex(reflect.ValueOf(name)); v.IsValid() { - return v - } - return reflect.Zero(typ.Elem()) - default: - return reflect.Value{} - } - } - return v -} - -// indirectPtr returns the item numLevels levels of indirection below the value. -// It is forgiving: if the value is not a pointer, it returns it rather than giving -// an error. If the pointer is nil, it is returned as is. -func indirectPtr(v reflect.Value, numLevels int) reflect.Value { - for i := numLevels; v.IsValid() && i > 0; i++ { - if p := v; p.Kind() == reflect.Ptr { - if p.IsNil() { - return v - } - v = p.Elem() - } else { - break - } - } - return v -} - -// Walk v through pointers and interfaces, extracting the elements within. -func indirect(v reflect.Value) reflect.Value { -loop: - for v.IsValid() { - switch av := v; av.Kind() { - case reflect.Ptr: - v = av.Elem() - case reflect.Interface: - v = av.Elem() - default: - break loop - } - } - return v -} - -// If the data for this template is a struct, find the named variable. -// Names of the form a.b.c are walked down the data tree. -// The special name "@" (the "cursor") denotes the current data. -// The value coming in (st.data) might need indirecting to reach -// a struct while the return value is not indirected - that is, -// it represents the actual named field. Leading stars indicate -// levels of indirection to be applied to the value. -func (t *Template) findVar(st *state, s string) reflect.Value { - data := st.data - flattenedName := strings.TrimLeft(s, "*") - numStars := len(s) - len(flattenedName) - s = flattenedName - if s == "@" { - return indirectPtr(data, numStars) - } - for _, elem := range strings.Split(s, ".", -1) { - // Look up field; data must be a struct or map. - data = t.lookup(st, data, elem) - if !data.IsValid() { - return reflect.Value{} - } - } - return indirectPtr(data, numStars) -} - -// Is there no data to look at? -func empty(v reflect.Value) bool { - v = indirect(v) - if !v.IsValid() { - return true - } - switch v.Kind() { - case reflect.Bool: - return v.Bool() == false - case reflect.String: - return v.String() == "" - case reflect.Struct: - return false - case reflect.Map: - return false - case reflect.Array: - return v.Len() == 0 - case reflect.Slice: - return v.Len() == 0 - } - return false -} - -// Look up a variable or method, up through the parent if necessary. -func (t *Template) varValue(name string, st *state) reflect.Value { - field := t.findVar(st, name) - if !field.IsValid() { - if st.parent == nil { - t.execError(st, t.linenum, "name not found: %s in type %s", name, st.data.Type()) - } - return t.varValue(name, st.parent) - } - return field -} - -func (t *Template) format(wr io.Writer, fmt string, val []interface{}, v *variableElement, st *state) { - fn := t.formatter(fmt) - if fn == nil { - t.execError(st, v.linenum, "missing formatter %s for variable", fmt) - } - fn(wr, fmt, val...) -} - -// Evaluate a variable, looking up through the parent if necessary. -// If it has a formatter attached ({var|formatter}) run that too. -func (t *Template) writeVariable(v *variableElement, st *state) { - // Resolve field names - val := make([]interface{}, len(v.args)) - for i, arg := range v.args { - if name, ok := arg.(fieldName); ok { - val[i] = t.varValue(string(name), st).Interface() - } else { - val[i] = arg - } - } - for i, fmt := range v.fmts[:len(v.fmts)-1] { - b := &st.buf[i&1] - b.Reset() - t.format(b, fmt, val, v, st) - val = val[0:1] - val[0] = b.Bytes() - } - t.format(st.wr, v.fmts[len(v.fmts)-1], val, v, st) -} - -// Execute element i. Return next index to execute. -func (t *Template) executeElement(i int, st *state) int { - switch elem := t.elems.At(i).(type) { - case *textElement: - st.wr.Write(elem.text) - return i + 1 - case *literalElement: - st.wr.Write(elem.text) - return i + 1 - case *variableElement: - t.writeVariable(elem, st) - return i + 1 - case *sectionElement: - t.executeSection(elem, st) - return elem.end - case *repeatedElement: - t.executeRepeated(elem, st) - return elem.end - } - e := t.elems.At(i) - t.execError(st, 0, "internal error: bad directive in execute: %v %T\n", reflect.ValueOf(e).Interface(), e) - return 0 -} - -// Execute the template. -func (t *Template) execute(start, end int, st *state) { - for i := start; i < end; { - i = t.executeElement(i, st) - } -} - -// Execute a .section -func (t *Template) executeSection(s *sectionElement, st *state) { - // Find driver data for this section. It must be in the current struct. - field := t.varValue(s.field, st) - if !field.IsValid() { - t.execError(st, s.linenum, ".section: cannot find field %s in %s", s.field, st.data.Type()) - } - st = st.clone(field) - start, end := s.start, s.or - if !empty(field) { - // Execute the normal block. - if end < 0 { - end = s.end - } - } else { - // Execute the .or block. If it's missing, do nothing. - start, end = s.or, s.end - if start < 0 { - return - } - } - for i := start; i < end; { - i = t.executeElement(i, st) - } -} - -// Return the result of calling the Iter method on v, or nil. -func iter(v reflect.Value) reflect.Value { - for j := 0; j < v.Type().NumMethod(); j++ { - mth := v.Type().Method(j) - fv := v.Method(j) - ft := fv.Type() - // TODO(rsc): NumIn() should return 0 here, because ft is from a curried FuncValue. - if mth.Name != "Iter" || ft.NumIn() != 1 || ft.NumOut() != 1 { - continue - } - ct := ft.Out(0) - if ct.Kind() != reflect.Chan || - ct.ChanDir()&reflect.RecvDir == 0 { - continue - } - return fv.Call(nil)[0] - } - return reflect.Value{} -} - -// Execute a .repeated section -func (t *Template) executeRepeated(r *repeatedElement, st *state) { - // Find driver data for this section. It must be in the current struct. - field := t.varValue(r.field, st) - if !field.IsValid() { - t.execError(st, r.linenum, ".repeated: cannot find field %s in %s", r.field, st.data.Type()) - } - field = indirect(field) - - start, end := r.start, r.or - if end < 0 { - end = r.end - } - if r.altstart >= 0 { - end = r.altstart - } - first := true - - // Code common to all the loops. - loopBody := func(newst *state) { - // .alternates between elements - if !first && r.altstart >= 0 { - for i := r.altstart; i < r.altend; { - i = t.executeElement(i, newst) - } - } - first = false - for i := start; i < end; { - i = t.executeElement(i, newst) - } - } - - if array := field; array.Kind() == reflect.Array || array.Kind() == reflect.Slice { - for j := 0; j < array.Len(); j++ { - loopBody(st.clone(array.Index(j))) - } - } else if m := field; m.Kind() == reflect.Map { - for _, key := range m.MapKeys() { - loopBody(st.clone(m.MapIndex(key))) - } - } else if ch := iter(field); ch.IsValid() { - for { - e, ok := ch.Recv() - if !ok { - break - } - loopBody(st.clone(e)) - } - } else { - t.execError(st, r.linenum, ".repeated: cannot repeat %s (type %s)", - r.field, field.Type()) - } - - if first { - // Empty. Execute the .or block, once. If it's missing, do nothing. - start, end := r.or, r.end - if start >= 0 { - newst := st.clone(field) - for i := start; i < end; { - i = t.executeElement(i, newst) - } - } - return - } -} - -// A valid delimiter must contain no space and be non-empty. -func validDelim(d []byte) bool { - if len(d) == 0 { - return false - } - for _, c := range d { - if isSpace(c) { - return false - } - } - return true -} - -// checkError is a deferred function to turn a panic with type *Error into a plain error return. -// Other panics are unexpected and so are re-enabled. -func checkError(error *os.Error) { - if v := recover(); v != nil { - if e, ok := v.(*Error); ok { - *error = e - } else { - // runtime errors should crash - panic(v) - } - } -} - // -- Public interface // Parse initializes a Template by parsing its definition. The string @@ -1100,7 +682,7 @@ func (t *Template) Execute(wr io.Writer, data interface{}) (err os.Error) { val := reflect.ValueOf(data) defer checkError(&err) t.p = 0 - t.execute(0, t.elems.Len(), &state{parent: nil, data: val, wr: wr}) + t.execute(0, len(t.elems), &state{parent: nil, data: val, wr: wr}) return nil } diff --git a/src/pkg/testing/benchmark.go b/src/pkg/testing/benchmark.go index db4c65941..f8b53e63a 100644 --- a/src/pkg/testing/benchmark.go +++ b/src/pkg/testing/benchmark.go @@ -143,7 +143,6 @@ func (b *B) run() BenchmarkResult { b.runN(n) } return BenchmarkResult{b.N, b.ns, b.bytes} - } // The results of a benchmark run. @@ -183,6 +182,7 @@ func RunBenchmarks(matchString func(pat, str string) (bool, os.Error), benchmark if len(*matchBenchmarks) == 0 { return } + procs := runtime.GOMAXPROCS(-1) for _, Benchmark := range benchmarks { matched, err := matchString(*matchBenchmarks, Benchmark.Name) if err != nil { @@ -194,7 +194,12 @@ func RunBenchmarks(matchString func(pat, str string) (bool, os.Error), benchmark } b := &B{benchmark: Benchmark} r := b.run() - fmt.Printf("%s\t%v\n", Benchmark.Name, r) + print(fmt.Sprintf("%s\t%v\n", Benchmark.Name, r)) + if p := runtime.GOMAXPROCS(-1); p != procs { + print(fmt.Sprintf("%s left GOMAXPROCS set to %d\n", Benchmark.Name, p)) + procs = p + } + } } diff --git a/src/pkg/testing/testing.go b/src/pkg/testing/testing.go index 8781b207d..3b2dd377a 100644 --- a/src/pkg/testing/testing.go +++ b/src/pkg/testing/testing.go @@ -171,6 +171,7 @@ func RunTests(matchString func(pat, str string) (bool, os.Error), tests []Intern if len(tests) == 0 { println("testing: warning: no tests to run") } + procs := runtime.GOMAXPROCS(-1) for i := 0; i < len(tests); i++ { matched, err := matchString(*match, tests[i].Name) if err != nil { @@ -190,6 +191,11 @@ func RunTests(matchString func(pat, str string) (bool, os.Error), tests []Intern <-t.ch ns += time.Nanoseconds() tstr := fmt.Sprintf("(%.2f seconds)", float64(ns)/1e9) + if p := runtime.GOMAXPROCS(-1); t.failed == false && p != procs { + t.failed = true + t.errors = fmt.Sprintf("%s left GOMAXPROCS set to %d\n", tests[i].Name, p) + procs = p + } if t.failed { println("--- FAIL:", tests[i].Name, tstr) print(t.errors) diff --git a/src/pkg/unicode/Makefile b/src/pkg/unicode/Makefile index 26e6e501f..55ed5b2d9 100644 --- a/src/pkg/unicode/Makefile +++ b/src/pkg/unicode/Makefile @@ -8,6 +8,7 @@ TARG=unicode GOFILES=\ casetables.go\ digit.go\ + graphic.go\ letter.go\ tables.go\ diff --git a/src/pkg/unicode/digit.go b/src/pkg/unicode/digit.go index 471c4dfdc..6793fd7e5 100644 --- a/src/pkg/unicode/digit.go +++ b/src/pkg/unicode/digit.go @@ -6,7 +6,7 @@ package unicode // IsDigit reports whether the rune is a decimal digit. func IsDigit(rune int) bool { - if rune < 0x100 { // quick ASCII (Latin-1, really) check + if rune <= MaxLatin1 { return '0' <= rune && rune <= '9' } return Is(Digit, rune) diff --git a/src/pkg/unicode/digit_test.go b/src/pkg/unicode/digit_test.go index 9bbccde92..ae3c0ece9 100644 --- a/src/pkg/unicode/digit_test.go +++ b/src/pkg/unicode/digit_test.go @@ -118,7 +118,7 @@ func TestDigit(t *testing.T) { // Test that the special case in IsDigit agrees with the table func TestDigitOptimization(t *testing.T) { - for i := 0; i < 0x100; i++ { + for i := 0; i <= MaxLatin1; i++ { if Is(Digit, i) != IsDigit(i) { t.Errorf("IsDigit(U+%04X) disagrees with Is(Digit)", i) } diff --git a/src/pkg/unicode/graphic.go b/src/pkg/unicode/graphic.go new file mode 100644 index 000000000..d482aace2 --- /dev/null +++ b/src/pkg/unicode/graphic.go @@ -0,0 +1,132 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package unicode + +// Bit masks for each code point under U+0100, for fast lookup. +const ( + pC = 1 << iota // a control character. + pP // a punctuation character. + pN // a numeral. + pS // a symbolic character. + pZ // a spacing character. + pLu // an upper-case letter. + pLl // a lower-case letter. + pp // a printable character according to Go's definition. + pg = pp | pZ // a graphical character according to the Unicode definition. +) + +// GraphicRanges defines the set of graphic characters according to Unicode. +var GraphicRanges = []*RangeTable{ + L, M, N, P, S, Zs, +} + +// PrintRanges defines the set of printable characters according to Go. +// ASCII space, U+0020, is handled separately. +var PrintRanges = []*RangeTable{ + L, M, N, P, S, +} + +// IsGraphic reports whether the rune is defined as a Graphic by Unicode. +// Such characters include letters, marks, numbers, punctuation, symbols, and +// spaces, from categories L, M, N, P, S, Zs. +func IsGraphic(rune int) bool { + // We cast to uint32 to avoid the extra test for negative, + // and in the index we cast to uint8 to avoid the range check. + if uint32(rune) <= MaxLatin1 { + return properties[uint8(rune)]&pg != 0 + } + return IsOneOf(GraphicRanges, rune) +} + +// IsPrint reports whether the rune is defined as printable by Go. Such +// characters include letters, marks, numbers, punctuation, symbols, and the +// ASCII space character, from categories L, M, N, P, S and the ASCII space +// character. This categorization is the same as IsGraphic except that the +// only spacing character is ASCII space, U+0020. +func IsPrint(rune int) bool { + if uint32(rune) <= MaxLatin1 { + return properties[uint8(rune)]&pp != 0 + } + return IsOneOf(PrintRanges, rune) +} + +// IsOneOf reports whether the rune is a member of one of the ranges. +// The rune is known to be above Latin-1. +func IsOneOf(set []*RangeTable, rune int) bool { + for _, inside := range set { + if Is(inside, rune) { + return true + } + } + return false +} + +// IsControl reports whether the rune is a control character. +// The C (Other) Unicode category includes more code points +// such as surrogates; use Is(C, rune) to test for them. +func IsControl(rune int) bool { + if uint32(rune) <= MaxLatin1 { + return properties[uint8(rune)]&pC != 0 + } + // All control characters are < Latin1Max. + return false +} + +// IsLetter reports whether the rune is a letter (category L). +func IsLetter(rune int) bool { + if uint32(rune) <= MaxLatin1 { + return properties[uint8(rune)]&(pLu|pLl) != 0 + } + return Is(Letter, rune) +} + +// IsMark reports whether the rune is a mark character (category M). +func IsMark(rune int) bool { + // There are no mark characters in Latin-1. + return Is(Mark, rune) +} + +// IsNumber reports whether the rune is a number (category N). +func IsNumber(rune int) bool { + if uint32(rune) <= MaxLatin1 { + return properties[uint8(rune)]&pN != 0 + } + return Is(Number, rune) +} + +// IsPunct reports whether the rune is a Unicode punctuation character +// (category P). +func IsPunct(rune int) bool { + if uint32(rune) <= MaxLatin1 { + return properties[uint8(rune)]&pP != 0 + } + return Is(Punct, rune) +} + +// IsSpace reports whether the rune is a space character as defined +// by Unicode's White Space property; in the Latin-1 space +// this is +// '\t', '\n', '\v', '\f', '\r', ' ', U+0085 (NEL), U+00A0 (NBSP). +// Other definitions of spacing characters are set by category +// Z and property Pattern_White_Space. +func IsSpace(rune int) bool { + // This property isn't the same as Z; special-case it. + if uint32(rune) <= MaxLatin1 { + switch rune { + case '\t', '\n', '\v', '\f', '\r', ' ', 0x85, 0xA0: + return true + } + return false + } + return Is(White_Space, rune) +} + +// IsSymbol reports whether the rune is a symbolic character. +func IsSymbol(rune int) bool { + if uint32(rune) <= MaxLatin1 { + return properties[uint8(rune)]&pS != 0 + } + return Is(Symbol, rune) +} diff --git a/src/pkg/unicode/graphic_test.go b/src/pkg/unicode/graphic_test.go new file mode 100644 index 000000000..77c679f7c --- /dev/null +++ b/src/pkg/unicode/graphic_test.go @@ -0,0 +1,122 @@ +// Copyright 2011 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package unicode_test + +import ( + "testing" + . "unicode" +) + +// Independently check that the special "Is" functions work +// in the Latin-1 range through the property table. + +func TestIsControlLatin1(t *testing.T) { + for i := 0; i <= MaxLatin1; i++ { + got := IsControl(i) + want := false + switch { + case 0x00 <= i && i <= 0x1F: + want = true + case 0x7F <= i && i <= 0x9F: + want = true + } + if got != want { + t.Errorf("%U incorrect: got %t; want %t", i, got, want) + } + } +} + +func TestIsLetterLatin1(t *testing.T) { + for i := 0; i <= MaxLatin1; i++ { + got := IsLetter(i) + want := Is(Letter, i) + if got != want { + t.Errorf("%U incorrect: got %t; want %t", i, got, want) + } + } +} + +func TestIsUpperLatin1(t *testing.T) { + for i := 0; i <= MaxLatin1; i++ { + got := IsUpper(i) + want := Is(Upper, i) + if got != want { + t.Errorf("%U incorrect: got %t; want %t", i, got, want) + } + } +} + +func TestIsLowerLatin1(t *testing.T) { + for i := 0; i <= MaxLatin1; i++ { + got := IsLower(i) + want := Is(Lower, i) + if got != want { + t.Errorf("%U incorrect: got %t; want %t", i, got, want) + } + } +} + +func TestNumberLatin1(t *testing.T) { + for i := 0; i <= MaxLatin1; i++ { + got := IsNumber(i) + want := Is(Number, i) + if got != want { + t.Errorf("%U incorrect: got %t; want %t", i, got, want) + } + } +} + +func TestIsPrintLatin1(t *testing.T) { + for i := 0; i <= MaxLatin1; i++ { + got := IsPrint(i) + want := IsOneOf(PrintRanges, i) + if i == ' ' { + want = true + } + if got != want { + t.Errorf("%U incorrect: got %t; want %t", i, got, want) + } + } +} + +func TestIsGraphicLatin1(t *testing.T) { + for i := 0; i <= MaxLatin1; i++ { + got := IsGraphic(i) + want := IsOneOf(GraphicRanges, i) + if got != want { + t.Errorf("%U incorrect: got %t; want %t", i, got, want) + } + } +} + +func TestIsPunctLatin1(t *testing.T) { + for i := 0; i <= MaxLatin1; i++ { + got := IsPunct(i) + want := Is(Punct, i) + if got != want { + t.Errorf("%U incorrect: got %t; want %t", i, got, want) + } + } +} + +func TestIsSpaceLatin1(t *testing.T) { + for i := 0; i <= MaxLatin1; i++ { + got := IsSpace(i) + want := Is(White_Space, i) + if got != want { + t.Errorf("%U incorrect: got %t; want %t", i, got, want) + } + } +} + +func TestIsSymbolLatin1(t *testing.T) { + for i := 0; i <= MaxLatin1; i++ { + got := IsSymbol(i) + want := Is(Symbol, i) + if got != want { + t.Errorf("%U incorrect: got %t; want %t", i, got, want) + } + } +} diff --git a/src/pkg/unicode/letter.go b/src/pkg/unicode/letter.go index 047bef19b..a0c55bbf7 100644 --- a/src/pkg/unicode/letter.go +++ b/src/pkg/unicode/letter.go @@ -9,6 +9,8 @@ package unicode const ( MaxRune = 0x10FFFF // Maximum valid Unicode code point. ReplacementChar = 0xFFFD // Represents invalid code points. + MaxASCII = 0x7F // maximum ASCII value. + MaxLatin1 = 0xFF // maximum Latin-1 value. ) // RangeTable defines a set of Unicode code points by listing the ranges of @@ -121,7 +123,7 @@ func is32(ranges []Range32, rune uint32) bool { // Is tests whether rune is in the specified table of ranges. func Is(rangeTab *RangeTable, rune int) bool { // common case: rune is ASCII or Latin-1. - if rune < 0x100 { + if uint32(rune) <= MaxLatin1 { // Only need to check R16, since R32 is always >= 1<<16. r16 := uint16(rune) for _, r := range rangeTab.R16 { @@ -148,49 +150,30 @@ func Is(rangeTab *RangeTable, rune int) bool { // IsUpper reports whether the rune is an upper case letter. func IsUpper(rune int) bool { - if rune < 0x80 { // quick ASCII check - return 'A' <= rune && rune <= 'Z' + // See comment in IsGraphic. + if uint32(rune) <= MaxLatin1 { + return properties[uint8(rune)]&pLu != 0 } return Is(Upper, rune) } // IsLower reports whether the rune is a lower case letter. func IsLower(rune int) bool { - if rune < 0x80 { // quick ASCII check - return 'a' <= rune && rune <= 'z' + // See comment in IsGraphic. + if uint32(rune) <= MaxLatin1 { + return properties[uint8(rune)]&pLl != 0 } return Is(Lower, rune) } // IsTitle reports whether the rune is a title case letter. func IsTitle(rune int) bool { - if rune < 0x80 { // quick ASCII check + if rune <= MaxLatin1 { return false } return Is(Title, rune) } -// IsLetter reports whether the rune is a letter. -func IsLetter(rune int) bool { - if rune < 0x80 { // quick ASCII check - rune &^= 'a' - 'A' - return 'A' <= rune && rune <= 'Z' - } - return Is(Letter, rune) -} - -// IsSpace reports whether the rune is a white space character. -func IsSpace(rune int) bool { - if rune <= 0xFF { // quick Latin-1 check - switch rune { - case '\t', '\n', '\v', '\f', '\r', ' ', 0x85, 0xA0: - return true - } - return false - } - return Is(White_Space, rune) -} - // to maps the rune using the specified case mapping. func to(_case int, rune int, caseRange []CaseRange) int { if _case < 0 || MaxCase <= _case { @@ -235,7 +218,7 @@ func To(_case int, rune int) int { // ToUpper maps the rune to upper case. func ToUpper(rune int) int { - if rune < 0x80 { // quick ASCII check + if rune <= MaxASCII { if 'a' <= rune && rune <= 'z' { rune -= 'a' - 'A' } @@ -246,7 +229,7 @@ func ToUpper(rune int) int { // ToLower maps the rune to lower case. func ToLower(rune int) int { - if rune < 0x80 { // quick ASCII check + if rune <= MaxASCII { if 'A' <= rune && rune <= 'Z' { rune += 'a' - 'A' } @@ -257,7 +240,7 @@ func ToLower(rune int) int { // ToTitle maps the rune to title case. func ToTitle(rune int) int { - if rune < 0x80 { // quick ASCII check + if rune <= MaxASCII { if 'a' <= rune && rune <= 'z' { // title case is upper case for ASCII rune -= 'a' - 'A' } diff --git a/src/pkg/unicode/letter_test.go b/src/pkg/unicode/letter_test.go index 432ffb671..4c24ffc51 100644 --- a/src/pkg/unicode/letter_test.go +++ b/src/pkg/unicode/letter_test.go @@ -323,7 +323,7 @@ func TestIsSpace(t *testing.T) { // Check that the optimizations for IsLetter etc. agree with the tables. // We only need to check the Latin-1 range. func TestLetterOptimizations(t *testing.T) { - for i := 0; i < 0x100; i++ { + for i := 0; i <= MaxLatin1; i++ { if Is(Letter, i) != IsLetter(i) { t.Errorf("IsLetter(U+%04X) disagrees with Is(Letter)", i) } diff --git a/src/pkg/unicode/maketables.go b/src/pkg/unicode/maketables.go index c3cf32b48..655fe46e4 100644 --- a/src/pkg/unicode/maketables.go +++ b/src/pkg/unicode/maketables.go @@ -28,6 +28,7 @@ func main() { printScriptOrProperty(false) printScriptOrProperty(true) printCases() + printLatinProperties() printSizes() } @@ -54,7 +55,17 @@ var test = flag.Bool("test", var scriptRe = regexp.MustCompile(`^([0-9A-F]+)(\.\.[0-9A-F]+)? *; ([A-Za-z_]+)$`) var logger = log.New(os.Stderr, "", log.Lshortfile) -var category = map[string]bool{"letter": true} // Nd Lu etc. letter is a special case +var category = map[string]bool{ + // Nd Lu etc. + // We use one-character names to identify merged categories + "L": true, // Lu Ll Lt Lm Lo + "P": true, // Pc Pd Ps Pe Pu Pf Po + "M": true, // Mn Mc Me + "N": true, // Nd Nl No + "S": true, // Sm Sc Sk So + "Z": true, // Zs Zl Zp + "C": true, // Cc Cf Cs Co Cn +} // UnicodeData.txt has form: // 0037;DIGIT SEVEN;Nd;0;EN;;7;7;7;N;;;;; @@ -247,12 +258,9 @@ func version() string { return "Unknown" } -func letterOp(code int) bool { - switch chars[code].category { - case "Lu", "Ll", "Lt", "Lm", "Lo": - return true - } - return false +func categoryOp(code int, class uint8) bool { + category := chars[code].category + return len(category) > 0 && category[0] == class } func loadChars() { @@ -348,8 +356,27 @@ func printCategories() { // Cases deserving special comments varDecl := "" switch name { - case "letter": - varDecl = "\tLetter = letter; // Letter is the set of Unicode letters.\n" + case "C": + varDecl = "\tOther = _C; // Other/C is the set of Unicode control and special characters, category C.\n" + varDecl += "\tC = _C\n" + case "L": + varDecl = "\tLetter = _L; // Letter/L is the set of Unicode letters, category L.\n" + varDecl += "\tL = _L\n" + case "M": + varDecl = "\tMark = _M; // Mark/M is the set of Unicode mark characters, category M.\n" + varDecl += "\tM = _M\n" + case "N": + varDecl = "\tNumber = _N; // Number/N is the set of Unicode number characters, category N.\n" + varDecl += "\tN = _N\n" + case "P": + varDecl = "\tPunct = _P; // Punct/P is the set of Unicode punctuation characters, category P.\n" + varDecl += "\tP = _P\n" + case "S": + varDecl = "\tSymbol = _S; // Symbol/S is the set of Unicode symbol characters, category S.\n" + varDecl += "\tS = _S\n" + case "Z": + varDecl = "\tSpace = _Z; // Space/Z is the set of Unicode space characters, category Z.\n" + varDecl += "\tZ = _Z\n" case "Nd": varDecl = "\tDigit = _Nd; // Digit is the set of Unicode characters with the \"decimal digit\" property.\n" case "Lu": @@ -359,17 +386,18 @@ func printCategories() { case "Lt": varDecl = "\tTitle = _Lt; // Title is the set of Unicode title case letters.\n" } - if name != "letter" { + if len(name) > 1 { varDecl += fmt.Sprintf( "\t%s = _%s; // %s is the set of Unicode characters in category %s.\n", name, name, name, name) } decl[ndecl] = varDecl ndecl++ - if name == "letter" { // special case + if len(name) == 1 { // unified categories + decl := fmt.Sprintf("var _%s = &RangeTable{\n", name) dumpRange( - "var letter = &RangeTable{\n", - letterOp) + decl, + func(code int) bool { return categoryOp(code, name[0]) }) continue } dumpRange( @@ -446,7 +474,7 @@ func printRange(lo, hi, stride uint32, size int, count *int) (int, *int) { if size == 16 && hi >= 1<<16 { if lo < 1<<16 { if lo+stride != hi { - log.Fatalf("unexpected straddle: %U %U %d", lo, hi, stride) + logger.Fatalf("unexpected straddle: %U %U %d", lo, hi, stride) } // No range contains U+FFFF as an instance, so split // the range into two entries. That way we can maintain @@ -472,11 +500,11 @@ func fullCategoryTest(list []string) { logger.Fatal("unknown category", name) } r, ok := unicode.Categories[name] - if !ok { - logger.Fatal("unknown table", name) + if !ok && len(name) > 1 { + logger.Fatalf("unknown table %q", name) } - if name == "letter" { - verifyRange(name, letterOp, r) + if len(name) == 1 { + verifyRange(name, func(code int) bool { return categoryOp(code, name[0]) }, r) } else { verifyRange( name, @@ -487,11 +515,16 @@ func fullCategoryTest(list []string) { } func verifyRange(name string, inCategory Op, table *unicode.RangeTable) { + count := 0 for i := range chars { web := inCategory(i) pkg := unicode.Is(table, i) if web != pkg { fmt.Fprintf(os.Stderr, "%s: %U: web=%t pkg=%t\n", name, i, web, pkg) + count++ + if count > 10 { + break + } } } } @@ -882,6 +915,42 @@ func fullCaseTest() { } } +func printLatinProperties() { + if *test { + return + } + fmt.Println("var properties = [MaxLatin1+1]uint8{") + for code := 0; code <= unicode.MaxLatin1; code++ { + var property string + switch chars[code].category { + case "Cc", "": // NUL has no category. + property = "pC" + case "Cf": // soft hyphen, unique category, not printable. + property = "0" + case "Ll": + property = "pLl | pp" + case "Lu": + property = "pLu | pp" + case "Nd", "No": + property = "pN | pp" + case "Pc", "Pd", "Pe", "Pf", "Pi", "Po", "Ps": + property = "pP | pp" + case "Sc", "Sk", "Sm", "So": + property = "pS | pp" + case "Zs": + property = "pZ" + default: + logger.Fatalf("%U has unknown category %q", code, chars[code].category) + } + // Special case + if code == ' ' { + property = "pZ | pp" + } + fmt.Printf("\t0x%.2X: %s, // %q\n", code, property, code) + } + fmt.Println("}") +} + var range16Count = 0 // Number of entries in the 16-bit range tables. var range32Count = 0 // Number of entries in the 32-bit range tables. diff --git a/src/pkg/unicode/script_test.go b/src/pkg/unicode/script_test.go index ff452b75c..b37ad1836 100644 --- a/src/pkg/unicode/script_test.go +++ b/src/pkg/unicode/script_test.go @@ -149,7 +149,14 @@ var inCategoryTest = []T{ {0x2028, "Zl"}, {0x2029, "Zp"}, {0x202f, "Zs"}, - {0x04aa, "letter"}, + // Unifieds. + {0x04aa, "L"}, + {0x0009, "C"}, + {0x1712, "M"}, + {0x0031, "N"}, + {0x00bb, "P"}, + {0x00a2, "S"}, + {0x00a0, "Z"}, } var inPropTest = []T{ @@ -197,13 +204,13 @@ func TestScripts(t *testing.T) { t.Fatal(test.script, "not a known script") } if !Is(Scripts[test.script], test.rune) { - t.Errorf("IsScript(%#x, %s) = false, want true", test.rune, test.script) + t.Errorf("IsScript(%U, %s) = false, want true", test.rune, test.script) } notTested[test.script] = false, false } for _, test := range outTest { if Is(Scripts[test.script], test.rune) { - t.Errorf("IsScript(%#x, %s) = true, want false", test.rune, test.script) + t.Errorf("IsScript(%U, %s) = true, want false", test.rune, test.script) } } for k := range notTested { @@ -221,7 +228,7 @@ func TestCategories(t *testing.T) { t.Fatal(test.script, "not a known category") } if !Is(Categories[test.script], test.rune) { - t.Errorf("IsCategory(%#x, %s) = false, want true", test.rune, test.script) + t.Errorf("IsCategory(%U, %s) = false, want true", test.rune, test.script) } notTested[test.script] = false, false } @@ -240,7 +247,7 @@ func TestProperties(t *testing.T) { t.Fatal(test.script, "not a known prop") } if !Is(Properties[test.script], test.rune) { - t.Errorf("IsCategory(%#x, %s) = false, want true", test.rune, test.script) + t.Errorf("IsCategory(%U, %s) = false, want true", test.rune, test.script) } notTested[test.script] = false, false } diff --git a/src/pkg/unicode/tables.go b/src/pkg/unicode/tables.go index fc2bdd8d2..32681a8c0 100644 --- a/src/pkg/unicode/tables.go +++ b/src/pkg/unicode/tables.go @@ -9,36 +9,42 @@ const Version = "6.0.0" // Categories is the set of Unicode data tables. var Categories = map[string]*RangeTable{ - "Lm": Lm, - "Ll": Ll, - "Me": Me, - "Mc": Mc, - "Mn": Mn, - "Zl": Zl, - "letter": letter, - "Zp": Zp, - "Zs": Zs, - "Cs": Cs, - "Co": Co, - "Cf": Cf, - "Cc": Cc, - "Po": Po, - "Pi": Pi, - "Pf": Pf, - "Pe": Pe, - "Pd": Pd, - "Pc": Pc, - "Ps": Ps, - "Nd": Nd, - "Nl": Nl, - "No": No, - "So": So, - "Sm": Sm, - "Sk": Sk, - "Sc": Sc, - "Lu": Lu, - "Lt": Lt, - "Lo": Lo, + "Lm": Lm, + "Ll": Ll, + "C": C, + "M": M, + "L": L, + "N": N, + "P": P, + "S": S, + "Z": Z, + "Me": Me, + "Mc": Mc, + "Mn": Mn, + "Zl": Zl, + "Zp": Zp, + "Zs": Zs, + "Cs": Cs, + "Co": Co, + "Cf": Cf, + "Cc": Cc, + "Po": Po, + "Pi": Pi, + "Pf": Pf, + "Pe": Pe, + "Pd": Pd, + "Pc": Pc, + "Ps": Ps, + "Nd": Nd, + "Nl": Nl, + "No": No, + "So": So, + "Sm": Sm, + "Sk": Sk, + "Sc": Sc, + "Lu": Lu, + "Lt": Lt, + "Lo": Lo, } var _Lm = &RangeTable{ @@ -226,127 +232,36 @@ var _Ll = &RangeTable{ }, } -var _Me = &RangeTable{ +var _C = &RangeTable{ R16: []Range16{ - {0x0488, 0x0489, 1}, - {0x20dd, 0x20e0, 1}, - {0x20e2, 0x20e4, 1}, - {0xa670, 0xa672, 1}, - }, -} - -var _Mc = &RangeTable{ - R16: []Range16{ - {0x0903, 0x093b, 56}, - {0x093e, 0x0940, 1}, - {0x0949, 0x094c, 1}, - {0x094e, 0x094f, 1}, - {0x0982, 0x0983, 1}, - {0x09be, 0x09c0, 1}, - {0x09c7, 0x09c8, 1}, - {0x09cb, 0x09cc, 1}, - {0x09d7, 0x0a03, 44}, - {0x0a3e, 0x0a40, 1}, - {0x0a83, 0x0abe, 59}, - {0x0abf, 0x0ac0, 1}, - {0x0ac9, 0x0acb, 2}, - {0x0acc, 0x0b02, 54}, - {0x0b03, 0x0b3e, 59}, - {0x0b40, 0x0b47, 7}, - {0x0b48, 0x0b4b, 3}, - {0x0b4c, 0x0b57, 11}, - {0x0bbe, 0x0bbf, 1}, - {0x0bc1, 0x0bc2, 1}, - {0x0bc6, 0x0bc8, 1}, - {0x0bca, 0x0bcc, 1}, - {0x0bd7, 0x0c01, 42}, - {0x0c02, 0x0c03, 1}, - {0x0c41, 0x0c44, 1}, - {0x0c82, 0x0c83, 1}, - {0x0cbe, 0x0cc0, 2}, - {0x0cc1, 0x0cc4, 1}, - {0x0cc7, 0x0cc8, 1}, - {0x0cca, 0x0ccb, 1}, - {0x0cd5, 0x0cd6, 1}, - {0x0d02, 0x0d03, 1}, - {0x0d3e, 0x0d40, 1}, - {0x0d46, 0x0d48, 1}, - {0x0d4a, 0x0d4c, 1}, - {0x0d57, 0x0d82, 43}, - {0x0d83, 0x0dcf, 76}, - {0x0dd0, 0x0dd1, 1}, - {0x0dd8, 0x0ddf, 1}, - {0x0df2, 0x0df3, 1}, - {0x0f3e, 0x0f3f, 1}, - {0x0f7f, 0x102b, 172}, - {0x102c, 0x1031, 5}, - {0x1038, 0x103b, 3}, - {0x103c, 0x1056, 26}, - {0x1057, 0x1062, 11}, - {0x1063, 0x1064, 1}, - {0x1067, 0x106d, 1}, - {0x1083, 0x1084, 1}, - {0x1087, 0x108c, 1}, - {0x108f, 0x109a, 11}, - {0x109b, 0x109c, 1}, - {0x17b6, 0x17be, 8}, - {0x17bf, 0x17c5, 1}, - {0x17c7, 0x17c8, 1}, - {0x1923, 0x1926, 1}, - {0x1929, 0x192b, 1}, - {0x1930, 0x1931, 1}, - {0x1933, 0x1938, 1}, - {0x19b0, 0x19c0, 1}, - {0x19c8, 0x19c9, 1}, - {0x1a19, 0x1a1b, 1}, - {0x1a55, 0x1a57, 2}, - {0x1a61, 0x1a63, 2}, - {0x1a64, 0x1a6d, 9}, - {0x1a6e, 0x1a72, 1}, - {0x1b04, 0x1b35, 49}, - {0x1b3b, 0x1b3d, 2}, - {0x1b3e, 0x1b41, 1}, - {0x1b43, 0x1b44, 1}, - {0x1b82, 0x1ba1, 31}, - {0x1ba6, 0x1ba7, 1}, - {0x1baa, 0x1be7, 61}, - {0x1bea, 0x1bec, 1}, - {0x1bee, 0x1bf2, 4}, - {0x1bf3, 0x1c24, 49}, - {0x1c25, 0x1c2b, 1}, - {0x1c34, 0x1c35, 1}, - {0x1ce1, 0x1cf2, 17}, - {0xa823, 0xa824, 1}, - {0xa827, 0xa880, 89}, - {0xa881, 0xa8b4, 51}, - {0xa8b5, 0xa8c3, 1}, - {0xa952, 0xa953, 1}, - {0xa983, 0xa9b4, 49}, - {0xa9b5, 0xa9ba, 5}, - {0xa9bb, 0xa9bd, 2}, - {0xa9be, 0xa9c0, 1}, - {0xaa2f, 0xaa30, 1}, - {0xaa33, 0xaa34, 1}, - {0xaa4d, 0xaa7b, 46}, - {0xabe3, 0xabe4, 1}, - {0xabe6, 0xabe7, 1}, - {0xabe9, 0xabea, 1}, - {0xabec, 0xabec, 1}, + {0x0001, 0x001f, 1}, + {0x007f, 0x009f, 1}, + {0x00ad, 0x0600, 1363}, + {0x0601, 0x0603, 1}, + {0x06dd, 0x070f, 50}, + {0x17b4, 0x17b5, 1}, + {0x200b, 0x200f, 1}, + {0x202a, 0x202e, 1}, + {0x2060, 0x2064, 1}, + {0x206a, 0x206f, 1}, + {0xd800, 0xf8ff, 1}, + {0xfeff, 0xfff9, 250}, + {0xfffa, 0xfffb, 1}, }, R32: []Range32{ - {0x11000, 0x11000, 1}, - {0x11002, 0x11082, 128}, - {0x110b0, 0x110b2, 1}, - {0x110b7, 0x110b8, 1}, - {0x1d165, 0x1d166, 1}, - {0x1d16d, 0x1d172, 1}, + {0x110bd, 0x1d173, 49334}, + {0x1d174, 0x1d17a, 1}, + {0xe0001, 0xe0020, 31}, + {0xe0021, 0xe007f, 1}, + {0xf0000, 0xffffd, 1}, + {0x100000, 0x10fffd, 1}, }, } -var _Mn = &RangeTable{ +var _M = &RangeTable{ R16: []Range16{ {0x0300, 0x036f, 1}, - {0x0483, 0x0487, 1}, + {0x0483, 0x0489, 1}, {0x0591, 0x05bd, 1}, {0x05bf, 0x05c1, 2}, {0x05c2, 0x05c4, 2}, @@ -367,49 +282,69 @@ var _Mn = &RangeTable{ {0x0825, 0x0827, 1}, {0x0829, 0x082d, 1}, {0x0859, 0x085b, 1}, - {0x0900, 0x0902, 1}, - {0x093a, 0x093c, 2}, - {0x0941, 0x0948, 1}, - {0x094d, 0x0951, 4}, - {0x0952, 0x0957, 1}, + {0x0900, 0x0903, 1}, + {0x093a, 0x093c, 1}, + {0x093e, 0x094f, 1}, + {0x0951, 0x0957, 1}, {0x0962, 0x0963, 1}, - {0x0981, 0x09bc, 59}, - {0x09c1, 0x09c4, 1}, - {0x09cd, 0x09e2, 21}, + {0x0981, 0x0983, 1}, + {0x09bc, 0x09be, 2}, + {0x09bf, 0x09c4, 1}, + {0x09c7, 0x09c8, 1}, + {0x09cb, 0x09cd, 1}, + {0x09d7, 0x09e2, 11}, {0x09e3, 0x0a01, 30}, - {0x0a02, 0x0a3c, 58}, - {0x0a41, 0x0a42, 1}, + {0x0a02, 0x0a03, 1}, + {0x0a3c, 0x0a3e, 2}, + {0x0a3f, 0x0a42, 1}, {0x0a47, 0x0a48, 1}, {0x0a4b, 0x0a4d, 1}, {0x0a51, 0x0a70, 31}, {0x0a71, 0x0a75, 4}, - {0x0a81, 0x0a82, 1}, - {0x0abc, 0x0ac1, 5}, - {0x0ac2, 0x0ac5, 1}, - {0x0ac7, 0x0ac8, 1}, - {0x0acd, 0x0ae2, 21}, - {0x0ae3, 0x0b01, 30}, - {0x0b3c, 0x0b3f, 3}, - {0x0b41, 0x0b44, 1}, - {0x0b4d, 0x0b56, 9}, + {0x0a81, 0x0a83, 1}, + {0x0abc, 0x0abe, 2}, + {0x0abf, 0x0ac5, 1}, + {0x0ac7, 0x0ac9, 1}, + {0x0acb, 0x0acd, 1}, + {0x0ae2, 0x0ae3, 1}, + {0x0b01, 0x0b03, 1}, + {0x0b3c, 0x0b3e, 2}, + {0x0b3f, 0x0b44, 1}, + {0x0b47, 0x0b48, 1}, + {0x0b4b, 0x0b4d, 1}, + {0x0b56, 0x0b57, 1}, {0x0b62, 0x0b63, 1}, - {0x0b82, 0x0bc0, 62}, - {0x0bcd, 0x0c3e, 113}, - {0x0c3f, 0x0c40, 1}, + {0x0b82, 0x0bbe, 60}, + {0x0bbf, 0x0bc2, 1}, + {0x0bc6, 0x0bc8, 1}, + {0x0bca, 0x0bcd, 1}, + {0x0bd7, 0x0c01, 42}, + {0x0c02, 0x0c03, 1}, + {0x0c3e, 0x0c44, 1}, {0x0c46, 0x0c48, 1}, {0x0c4a, 0x0c4d, 1}, {0x0c55, 0x0c56, 1}, {0x0c62, 0x0c63, 1}, - {0x0cbc, 0x0cbf, 3}, - {0x0cc6, 0x0ccc, 6}, - {0x0ccd, 0x0ce2, 21}, - {0x0ce3, 0x0d41, 94}, - {0x0d42, 0x0d44, 1}, - {0x0d4d, 0x0d62, 21}, - {0x0d63, 0x0dca, 103}, - {0x0dd2, 0x0dd4, 1}, - {0x0dd6, 0x0e31, 91}, - {0x0e34, 0x0e3a, 1}, + {0x0c82, 0x0c83, 1}, + {0x0cbc, 0x0cbe, 2}, + {0x0cbf, 0x0cc4, 1}, + {0x0cc6, 0x0cc8, 1}, + {0x0cca, 0x0ccd, 1}, + {0x0cd5, 0x0cd6, 1}, + {0x0ce2, 0x0ce3, 1}, + {0x0d02, 0x0d03, 1}, + {0x0d3e, 0x0d44, 1}, + {0x0d46, 0x0d48, 1}, + {0x0d4a, 0x0d4d, 1}, + {0x0d57, 0x0d62, 11}, + {0x0d63, 0x0d82, 31}, + {0x0d83, 0x0dca, 71}, + {0x0dcf, 0x0dd4, 1}, + {0x0dd6, 0x0dd8, 2}, + {0x0dd9, 0x0ddf, 1}, + {0x0df2, 0x0df3, 1}, + {0x0e31, 0x0e34, 3}, + {0x0e35, 0x0e3a, 1}, {0x0e47, 0x0e4e, 1}, {0x0eb1, 0x0eb4, 3}, {0x0eb5, 0x0eb9, 1}, @@ -417,94 +352,79 @@ var _Mn = &RangeTable{ {0x0ec8, 0x0ecd, 1}, {0x0f18, 0x0f19, 1}, {0x0f35, 0x0f39, 2}, - {0x0f71, 0x0f7e, 1}, - {0x0f80, 0x0f84, 1}, + {0x0f3e, 0x0f3f, 1}, + {0x0f71, 0x0f84, 1}, {0x0f86, 0x0f87, 1}, {0x0f8d, 0x0f97, 1}, {0x0f99, 0x0fbc, 1}, - {0x0fc6, 0x102d, 103}, - {0x102e, 0x1030, 1}, - {0x1032, 0x1037, 1}, - {0x1039, 0x103a, 1}, - {0x103d, 0x103e, 1}, - {0x1058, 0x1059, 1}, + {0x0fc6, 0x102b, 101}, + {0x102c, 0x103e, 1}, + {0x1056, 0x1059, 1}, {0x105e, 0x1060, 1}, + {0x1062, 0x1064, 1}, + {0x1067, 0x106d, 1}, {0x1071, 0x1074, 1}, - {0x1082, 0x1085, 3}, - {0x1086, 0x108d, 7}, - {0x109d, 0x135d, 704}, - {0x135e, 0x135f, 1}, + {0x1082, 0x108d, 1}, + {0x108f, 0x109a, 11}, + {0x109b, 0x109d, 1}, + {0x135d, 0x135f, 1}, {0x1712, 0x1714, 1}, {0x1732, 0x1734, 1}, {0x1752, 0x1753, 1}, {0x1772, 0x1773, 1}, - {0x17b7, 0x17bd, 1}, - {0x17c6, 0x17c9, 3}, - {0x17ca, 0x17d3, 1}, + {0x17b6, 0x17d3, 1}, {0x17dd, 0x180b, 46}, {0x180c, 0x180d, 1}, {0x18a9, 0x1920, 119}, - {0x1921, 0x1922, 1}, - {0x1927, 0x1928, 1}, - {0x1932, 0x1939, 7}, - {0x193a, 0x193b, 1}, - {0x1a17, 0x1a18, 1}, - {0x1a56, 0x1a58, 2}, - {0x1a59, 0x1a5e, 1}, - {0x1a60, 0x1a62, 2}, - {0x1a65, 0x1a6c, 1}, - {0x1a73, 0x1a7c, 1}, + {0x1921, 0x192b, 1}, + {0x1930, 0x193b, 1}, + {0x19b0, 0x19c0, 1}, + {0x19c8, 0x19c9, 1}, + {0x1a17, 0x1a1b, 1}, + {0x1a55, 0x1a5e, 1}, + {0x1a60, 0x1a7c, 1}, {0x1a7f, 0x1b00, 129}, - {0x1b01, 0x1b03, 1}, - {0x1b34, 0x1b36, 2}, - {0x1b37, 0x1b3a, 1}, - {0x1b3c, 0x1b42, 6}, + {0x1b01, 0x1b04, 1}, + {0x1b34, 0x1b44, 1}, {0x1b6b, 0x1b73, 1}, - {0x1b80, 0x1b81, 1}, - {0x1ba2, 0x1ba5, 1}, - {0x1ba8, 0x1ba9, 1}, - {0x1be6, 0x1be8, 2}, - {0x1be9, 0x1bed, 4}, - {0x1bef, 0x1bf1, 1}, - {0x1c2c, 0x1c33, 1}, - {0x1c36, 0x1c37, 1}, + {0x1b80, 0x1b82, 1}, + {0x1ba1, 0x1baa, 1}, + {0x1be6, 0x1bf3, 1}, + {0x1c24, 0x1c37, 1}, {0x1cd0, 0x1cd2, 1}, - {0x1cd4, 0x1ce0, 1}, - {0x1ce2, 0x1ce8, 1}, - {0x1ced, 0x1dc0, 211}, - {0x1dc1, 0x1de6, 1}, + {0x1cd4, 0x1ce8, 1}, + {0x1ced, 0x1cf2, 5}, + {0x1dc0, 0x1de6, 1}, {0x1dfc, 0x1dff, 1}, - {0x20d0, 0x20dc, 1}, - {0x20e1, 0x20e5, 4}, - {0x20e6, 0x20f0, 1}, + {0x20d0, 0x20f0, 1}, {0x2cef, 0x2cf1, 1}, {0x2d7f, 0x2de0, 97}, {0x2de1, 0x2dff, 1}, {0x302a, 0x302f, 1}, {0x3099, 0x309a, 1}, - {0xa66f, 0xa67c, 13}, - {0xa67d, 0xa6f0, 115}, - {0xa6f1, 0xa802, 273}, - {0xa806, 0xa80b, 5}, - {0xa825, 0xa826, 1}, - {0xa8c4, 0xa8e0, 28}, - {0xa8e1, 0xa8f1, 1}, + {0xa66f, 0xa672, 1}, + {0xa67c, 0xa67d, 1}, + {0xa6f0, 0xa6f1, 1}, + {0xa802, 0xa806, 4}, + {0xa80b, 0xa823, 24}, + {0xa824, 0xa827, 1}, + {0xa880, 0xa881, 1}, + {0xa8b4, 0xa8c4, 1}, + {0xa8e0, 0xa8f1, 1}, {0xa926, 0xa92d, 1}, - {0xa947, 0xa951, 1}, - {0xa980, 0xa982, 1}, - {0xa9b3, 0xa9b6, 3}, - {0xa9b7, 0xa9b9, 1}, - {0xa9bc, 0xaa29, 109}, - {0xaa2a, 0xaa2e, 1}, - {0xaa31, 0xaa32, 1}, - {0xaa35, 0xaa36, 1}, + {0xa947, 0xa953, 1}, + {0xa980, 0xa983, 1}, + {0xa9b3, 0xa9c0, 1}, + {0xaa29, 0xaa36, 1}, {0xaa43, 0xaa4c, 9}, + {0xaa4d, 0xaa7b, 46}, {0xaab0, 0xaab2, 2}, {0xaab3, 0xaab4, 1}, {0xaab7, 0xaab8, 1}, {0xaabe, 0xaabf, 1}, - {0xaac1, 0xabe5, 292}, - {0xabe8, 0xabed, 5}, + {0xaac1, 0xabe3, 290}, + {0xabe4, 0xabea, 1}, + {0xabec, 0xabed, 1}, {0xfb1e, 0xfe00, 738}, {0xfe01, 0xfe0f, 1}, {0xfe20, 0xfe26, 1}, @@ -515,12 +435,13 @@ var _Mn = &RangeTable{ {0x10a05, 0x10a06, 1}, {0x10a0c, 0x10a0f, 1}, {0x10a38, 0x10a3a, 1}, - {0x10a3f, 0x11001, 1474}, + {0x10a3f, 0x11000, 1473}, + {0x11001, 0x11002, 1}, {0x11038, 0x11046, 1}, - {0x11080, 0x11081, 1}, - {0x110b3, 0x110b6, 1}, - {0x110b9, 0x110ba, 1}, - {0x1d167, 0x1d169, 1}, + {0x11080, 0x11082, 1}, + {0x110b0, 0x110ba, 1}, + {0x1d165, 0x1d169, 1}, + {0x1d16d, 0x1d172, 1}, {0x1d17b, 0x1d182, 1}, {0x1d185, 0x1d18b, 1}, {0x1d1aa, 0x1d1ad, 1}, @@ -529,13 +450,7 @@ var _Mn = &RangeTable{ }, } -var _Zl = &RangeTable{ - R16: []Range16{ - {0x2028, 0x2028, 1}, - }, -} - -var letter = &RangeTable{ +var _L = &RangeTable{ R16: []Range16{ {0x0041, 0x005a, 1}, {0x0061, 0x007a, 1}, @@ -956,6 +871,725 @@ var letter = &RangeTable{ }, } +var _N = &RangeTable{ + R16: []Range16{ + {0x0030, 0x0039, 1}, + {0x00b2, 0x00b3, 1}, + {0x00b9, 0x00bc, 3}, + {0x00bd, 0x00be, 1}, + {0x0660, 0x0669, 1}, + {0x06f0, 0x06f9, 1}, + {0x07c0, 0x07c9, 1}, + {0x0966, 0x096f, 1}, + {0x09e6, 0x09ef, 1}, + {0x09f4, 0x09f9, 1}, + {0x0a66, 0x0a6f, 1}, + {0x0ae6, 0x0aef, 1}, + {0x0b66, 0x0b6f, 1}, + {0x0b72, 0x0b77, 1}, + {0x0be6, 0x0bf2, 1}, + {0x0c66, 0x0c6f, 1}, + {0x0c78, 0x0c7e, 1}, + {0x0ce6, 0x0cef, 1}, + {0x0d66, 0x0d75, 1}, + {0x0e50, 0x0e59, 1}, + {0x0ed0, 0x0ed9, 1}, + {0x0f20, 0x0f33, 1}, + {0x1040, 0x1049, 1}, + {0x1090, 0x1099, 1}, + {0x1369, 0x137c, 1}, + {0x16ee, 0x16f0, 1}, + {0x17e0, 0x17e9, 1}, + {0x17f0, 0x17f9, 1}, + {0x1810, 0x1819, 1}, + {0x1946, 0x194f, 1}, + {0x19d0, 0x19da, 1}, + {0x1a80, 0x1a89, 1}, + {0x1a90, 0x1a99, 1}, + {0x1b50, 0x1b59, 1}, + {0x1bb0, 0x1bb9, 1}, + {0x1c40, 0x1c49, 1}, + {0x1c50, 0x1c59, 1}, + {0x2070, 0x2074, 4}, + {0x2075, 0x2079, 1}, + {0x2080, 0x2089, 1}, + {0x2150, 0x2182, 1}, + {0x2185, 0x2189, 1}, + {0x2460, 0x249b, 1}, + {0x24ea, 0x24ff, 1}, + {0x2776, 0x2793, 1}, + {0x2cfd, 0x3007, 778}, + {0x3021, 0x3029, 1}, + {0x3038, 0x303a, 1}, + {0x3192, 0x3195, 1}, + {0x3220, 0x3229, 1}, + {0x3251, 0x325f, 1}, + {0x3280, 0x3289, 1}, + {0x32b1, 0x32bf, 1}, + {0xa620, 0xa629, 1}, + {0xa6e6, 0xa6ef, 1}, + {0xa830, 0xa835, 1}, + {0xa8d0, 0xa8d9, 1}, + {0xa900, 0xa909, 1}, + {0xa9d0, 0xa9d9, 1}, + {0xaa50, 0xaa59, 1}, + {0xabf0, 0xabf9, 1}, + {0xff10, 0xff19, 1}, + }, + R32: []Range32{ + {0x10107, 0x10133, 1}, + {0x10140, 0x10178, 1}, + {0x1018a, 0x10320, 406}, + {0x10321, 0x10323, 1}, + {0x10341, 0x1034a, 9}, + {0x103d1, 0x103d5, 1}, + {0x104a0, 0x104a9, 1}, + {0x10858, 0x1085f, 1}, + {0x10916, 0x1091b, 1}, + {0x10a40, 0x10a47, 1}, + {0x10a7d, 0x10a7e, 1}, + {0x10b58, 0x10b5f, 1}, + {0x10b78, 0x10b7f, 1}, + {0x10e60, 0x10e7e, 1}, + {0x11052, 0x1106f, 1}, + {0x12400, 0x12462, 1}, + {0x1d360, 0x1d371, 1}, + {0x1d7ce, 0x1d7ff, 1}, + {0x1f100, 0x1f10a, 1}, + }, +} + +var _P = &RangeTable{ + R16: []Range16{ + {0x0021, 0x0023, 1}, + {0x0025, 0x002a, 1}, + {0x002c, 0x002f, 1}, + {0x003a, 0x003b, 1}, + {0x003f, 0x0040, 1}, + {0x005b, 0x005d, 1}, + {0x005f, 0x007b, 28}, + {0x007d, 0x00a1, 36}, + {0x00ab, 0x00b7, 12}, + {0x00bb, 0x00bf, 4}, + {0x037e, 0x0387, 9}, + {0x055a, 0x055f, 1}, + {0x0589, 0x058a, 1}, + {0x05be, 0x05c0, 2}, + {0x05c3, 0x05c6, 3}, + {0x05f3, 0x05f4, 1}, + {0x0609, 0x060a, 1}, + {0x060c, 0x060d, 1}, + {0x061b, 0x061e, 3}, + {0x061f, 0x066a, 75}, + {0x066b, 0x066d, 1}, + {0x06d4, 0x0700, 44}, + {0x0701, 0x070d, 1}, + {0x07f7, 0x07f9, 1}, + {0x0830, 0x083e, 1}, + {0x085e, 0x0964, 262}, + {0x0965, 0x0970, 11}, + {0x0df4, 0x0e4f, 91}, + {0x0e5a, 0x0e5b, 1}, + {0x0f04, 0x0f12, 1}, + {0x0f3a, 0x0f3d, 1}, + {0x0f85, 0x0fd0, 75}, + {0x0fd1, 0x0fd4, 1}, + {0x0fd9, 0x0fda, 1}, + {0x104a, 0x104f, 1}, + {0x10fb, 0x1361, 614}, + {0x1362, 0x1368, 1}, + {0x1400, 0x166d, 621}, + {0x166e, 0x169b, 45}, + {0x169c, 0x16eb, 79}, + {0x16ec, 0x16ed, 1}, + {0x1735, 0x1736, 1}, + {0x17d4, 0x17d6, 1}, + {0x17d8, 0x17da, 1}, + {0x1800, 0x180a, 1}, + {0x1944, 0x1945, 1}, + {0x1a1e, 0x1a1f, 1}, + {0x1aa0, 0x1aa6, 1}, + {0x1aa8, 0x1aad, 1}, + {0x1b5a, 0x1b60, 1}, + {0x1bfc, 0x1bff, 1}, + {0x1c3b, 0x1c3f, 1}, + {0x1c7e, 0x1c7f, 1}, + {0x1cd3, 0x2010, 829}, + {0x2011, 0x2027, 1}, + {0x2030, 0x2043, 1}, + {0x2045, 0x2051, 1}, + {0x2053, 0x205e, 1}, + {0x207d, 0x207e, 1}, + {0x208d, 0x208e, 1}, + {0x2329, 0x232a, 1}, + {0x2768, 0x2775, 1}, + {0x27c5, 0x27c6, 1}, + {0x27e6, 0x27ef, 1}, + {0x2983, 0x2998, 1}, + {0x29d8, 0x29db, 1}, + {0x29fc, 0x29fd, 1}, + {0x2cf9, 0x2cfc, 1}, + {0x2cfe, 0x2cff, 1}, + {0x2d70, 0x2e00, 144}, + {0x2e01, 0x2e2e, 1}, + {0x2e30, 0x2e31, 1}, + {0x3001, 0x3003, 1}, + {0x3008, 0x3011, 1}, + {0x3014, 0x301f, 1}, + {0x3030, 0x303d, 13}, + {0x30a0, 0x30fb, 91}, + {0xa4fe, 0xa4ff, 1}, + {0xa60d, 0xa60f, 1}, + {0xa673, 0xa67e, 11}, + {0xa6f2, 0xa6f7, 1}, + {0xa874, 0xa877, 1}, + {0xa8ce, 0xa8cf, 1}, + {0xa8f8, 0xa8fa, 1}, + {0xa92e, 0xa92f, 1}, + {0xa95f, 0xa9c1, 98}, + {0xa9c2, 0xa9cd, 1}, + {0xa9de, 0xa9df, 1}, + {0xaa5c, 0xaa5f, 1}, + {0xaade, 0xaadf, 1}, + {0xabeb, 0xfd3e, 20819}, + {0xfd3f, 0xfe10, 209}, + {0xfe11, 0xfe19, 1}, + {0xfe30, 0xfe52, 1}, + {0xfe54, 0xfe61, 1}, + {0xfe63, 0xfe68, 5}, + {0xfe6a, 0xfe6b, 1}, + {0xff01, 0xff03, 1}, + {0xff05, 0xff0a, 1}, + {0xff0c, 0xff0f, 1}, + {0xff1a, 0xff1b, 1}, + {0xff1f, 0xff20, 1}, + {0xff3b, 0xff3d, 1}, + {0xff3f, 0xff5b, 28}, + {0xff5d, 0xff5f, 2}, + {0xff60, 0xff65, 1}, + }, + R32: []Range32{ + {0x10100, 0x10101, 1}, + {0x1039f, 0x103d0, 49}, + {0x10857, 0x1091f, 200}, + {0x1093f, 0x10a50, 273}, + {0x10a51, 0x10a58, 1}, + {0x10a7f, 0x10b39, 186}, + {0x10b3a, 0x10b3f, 1}, + {0x11047, 0x1104d, 1}, + {0x110bb, 0x110bc, 1}, + {0x110be, 0x110c1, 1}, + {0x12470, 0x12473, 1}, + }, +} + +var _S = &RangeTable{ + R16: []Range16{ + {0x0024, 0x002b, 7}, + {0x003c, 0x003e, 1}, + {0x005e, 0x0060, 2}, + {0x007c, 0x007e, 2}, + {0x00a2, 0x00a9, 1}, + {0x00ac, 0x00ae, 2}, + {0x00af, 0x00b1, 1}, + {0x00b4, 0x00b8, 2}, + {0x00d7, 0x00f7, 32}, + {0x02c2, 0x02c5, 1}, + {0x02d2, 0x02df, 1}, + {0x02e5, 0x02eb, 1}, + {0x02ed, 0x02ef, 2}, + {0x02f0, 0x02ff, 1}, + {0x0375, 0x0384, 15}, + {0x0385, 0x03f6, 113}, + {0x0482, 0x0606, 388}, + {0x0607, 0x0608, 1}, + {0x060b, 0x060e, 3}, + {0x060f, 0x06de, 207}, + {0x06e9, 0x06fd, 20}, + {0x06fe, 0x07f6, 248}, + {0x09f2, 0x09f3, 1}, + {0x09fa, 0x09fb, 1}, + {0x0af1, 0x0b70, 127}, + {0x0bf3, 0x0bfa, 1}, + {0x0c7f, 0x0d79, 250}, + {0x0e3f, 0x0f01, 194}, + {0x0f02, 0x0f03, 1}, + {0x0f13, 0x0f17, 1}, + {0x0f1a, 0x0f1f, 1}, + {0x0f34, 0x0f38, 2}, + {0x0fbe, 0x0fc5, 1}, + {0x0fc7, 0x0fcc, 1}, + {0x0fce, 0x0fcf, 1}, + {0x0fd5, 0x0fd8, 1}, + {0x109e, 0x109f, 1}, + {0x1360, 0x1390, 48}, + {0x1391, 0x1399, 1}, + {0x17db, 0x1940, 357}, + {0x19de, 0x19ff, 1}, + {0x1b61, 0x1b6a, 1}, + {0x1b74, 0x1b7c, 1}, + {0x1fbd, 0x1fbf, 2}, + {0x1fc0, 0x1fc1, 1}, + {0x1fcd, 0x1fcf, 1}, + {0x1fdd, 0x1fdf, 1}, + {0x1fed, 0x1fef, 1}, + {0x1ffd, 0x1ffe, 1}, + {0x2044, 0x2052, 14}, + {0x207a, 0x207c, 1}, + {0x208a, 0x208c, 1}, + {0x20a0, 0x20b9, 1}, + {0x2100, 0x2101, 1}, + {0x2103, 0x2106, 1}, + {0x2108, 0x2109, 1}, + {0x2114, 0x2116, 2}, + {0x2117, 0x2118, 1}, + {0x211e, 0x2123, 1}, + {0x2125, 0x2129, 2}, + {0x212e, 0x213a, 12}, + {0x213b, 0x2140, 5}, + {0x2141, 0x2144, 1}, + {0x214a, 0x214d, 1}, + {0x214f, 0x2190, 65}, + {0x2191, 0x2328, 1}, + {0x232b, 0x23f3, 1}, + {0x2400, 0x2426, 1}, + {0x2440, 0x244a, 1}, + {0x249c, 0x24e9, 1}, + {0x2500, 0x26ff, 1}, + {0x2701, 0x2767, 1}, + {0x2794, 0x27c4, 1}, + {0x27c7, 0x27ca, 1}, + {0x27cc, 0x27ce, 2}, + {0x27cf, 0x27e5, 1}, + {0x27f0, 0x2982, 1}, + {0x2999, 0x29d7, 1}, + {0x29dc, 0x29fb, 1}, + {0x29fe, 0x2b4c, 1}, + {0x2b50, 0x2b59, 1}, + {0x2ce5, 0x2cea, 1}, + {0x2e80, 0x2e99, 1}, + {0x2e9b, 0x2ef3, 1}, + {0x2f00, 0x2fd5, 1}, + {0x2ff0, 0x2ffb, 1}, + {0x3004, 0x3012, 14}, + {0x3013, 0x3020, 13}, + {0x3036, 0x3037, 1}, + {0x303e, 0x303f, 1}, + {0x309b, 0x309c, 1}, + {0x3190, 0x3191, 1}, + {0x3196, 0x319f, 1}, + {0x31c0, 0x31e3, 1}, + {0x3200, 0x321e, 1}, + {0x322a, 0x3250, 1}, + {0x3260, 0x327f, 1}, + {0x328a, 0x32b0, 1}, + {0x32c0, 0x32fe, 1}, + {0x3300, 0x33ff, 1}, + {0x4dc0, 0x4dff, 1}, + {0xa490, 0xa4c6, 1}, + {0xa700, 0xa716, 1}, + {0xa720, 0xa721, 1}, + {0xa789, 0xa78a, 1}, + {0xa828, 0xa82b, 1}, + {0xa836, 0xa839, 1}, + {0xaa77, 0xaa79, 1}, + {0xfb29, 0xfbb2, 137}, + {0xfbb3, 0xfbc1, 1}, + {0xfdfc, 0xfdfd, 1}, + {0xfe62, 0xfe64, 2}, + {0xfe65, 0xfe66, 1}, + {0xfe69, 0xff04, 155}, + {0xff0b, 0xff1c, 17}, + {0xff1d, 0xff1e, 1}, + {0xff3e, 0xff40, 2}, + {0xff5c, 0xff5e, 2}, + {0xffe0, 0xffe6, 1}, + {0xffe8, 0xffee, 1}, + {0xfffc, 0xfffd, 1}, + }, + R32: []Range32{ + {0x10102, 0x10137, 53}, + {0x10138, 0x1013f, 1}, + {0x10179, 0x10189, 1}, + {0x10190, 0x1019b, 1}, + {0x101d0, 0x101fc, 1}, + {0x1d000, 0x1d0f5, 1}, + {0x1d100, 0x1d126, 1}, + {0x1d129, 0x1d164, 1}, + {0x1d16a, 0x1d16c, 1}, + {0x1d183, 0x1d184, 1}, + {0x1d18c, 0x1d1a9, 1}, + {0x1d1ae, 0x1d1dd, 1}, + {0x1d200, 0x1d241, 1}, + {0x1d245, 0x1d300, 187}, + {0x1d301, 0x1d356, 1}, + {0x1d6c1, 0x1d6db, 26}, + {0x1d6fb, 0x1d715, 26}, + {0x1d735, 0x1d74f, 26}, + {0x1d76f, 0x1d789, 26}, + {0x1d7a9, 0x1d7c3, 26}, + {0x1f000, 0x1f02b, 1}, + {0x1f030, 0x1f093, 1}, + {0x1f0a0, 0x1f0ae, 1}, + {0x1f0b1, 0x1f0be, 1}, + {0x1f0c1, 0x1f0cf, 1}, + {0x1f0d1, 0x1f0df, 1}, + {0x1f110, 0x1f12e, 1}, + {0x1f130, 0x1f169, 1}, + {0x1f170, 0x1f19a, 1}, + {0x1f1e6, 0x1f202, 1}, + {0x1f210, 0x1f23a, 1}, + {0x1f240, 0x1f248, 1}, + {0x1f250, 0x1f251, 1}, + {0x1f300, 0x1f320, 1}, + {0x1f330, 0x1f335, 1}, + {0x1f337, 0x1f37c, 1}, + {0x1f380, 0x1f393, 1}, + {0x1f3a0, 0x1f3c4, 1}, + {0x1f3c6, 0x1f3ca, 1}, + {0x1f3e0, 0x1f3f0, 1}, + {0x1f400, 0x1f43e, 1}, + {0x1f440, 0x1f442, 2}, + {0x1f443, 0x1f4f7, 1}, + {0x1f4f9, 0x1f4fc, 1}, + {0x1f500, 0x1f53d, 1}, + {0x1f550, 0x1f567, 1}, + {0x1f5fb, 0x1f5ff, 1}, + {0x1f601, 0x1f610, 1}, + {0x1f612, 0x1f614, 1}, + {0x1f616, 0x1f61c, 2}, + {0x1f61d, 0x1f61e, 1}, + {0x1f620, 0x1f625, 1}, + {0x1f628, 0x1f62b, 1}, + {0x1f62d, 0x1f630, 3}, + {0x1f631, 0x1f633, 1}, + {0x1f635, 0x1f640, 1}, + {0x1f645, 0x1f64f, 1}, + {0x1f680, 0x1f6c5, 1}, + {0x1f700, 0x1f773, 1}, + }, +} + +var _Z = &RangeTable{ + R16: []Range16{ + {0x0020, 0x00a0, 128}, + {0x1680, 0x180e, 398}, + {0x2000, 0x200a, 1}, + {0x2028, 0x2029, 1}, + {0x202f, 0x205f, 48}, + {0x3000, 0x3000, 1}, + }, +} + +var _Me = &RangeTable{ + R16: []Range16{ + {0x0488, 0x0489, 1}, + {0x20dd, 0x20e0, 1}, + {0x20e2, 0x20e4, 1}, + {0xa670, 0xa672, 1}, + }, +} + +var _Mc = &RangeTable{ + R16: []Range16{ + {0x0903, 0x093b, 56}, + {0x093e, 0x0940, 1}, + {0x0949, 0x094c, 1}, + {0x094e, 0x094f, 1}, + {0x0982, 0x0983, 1}, + {0x09be, 0x09c0, 1}, + {0x09c7, 0x09c8, 1}, + {0x09cb, 0x09cc, 1}, + {0x09d7, 0x0a03, 44}, + {0x0a3e, 0x0a40, 1}, + {0x0a83, 0x0abe, 59}, + {0x0abf, 0x0ac0, 1}, + {0x0ac9, 0x0acb, 2}, + {0x0acc, 0x0b02, 54}, + {0x0b03, 0x0b3e, 59}, + {0x0b40, 0x0b47, 7}, + {0x0b48, 0x0b4b, 3}, + {0x0b4c, 0x0b57, 11}, + {0x0bbe, 0x0bbf, 1}, + {0x0bc1, 0x0bc2, 1}, + {0x0bc6, 0x0bc8, 1}, + {0x0bca, 0x0bcc, 1}, + {0x0bd7, 0x0c01, 42}, + {0x0c02, 0x0c03, 1}, + {0x0c41, 0x0c44, 1}, + {0x0c82, 0x0c83, 1}, + {0x0cbe, 0x0cc0, 2}, + {0x0cc1, 0x0cc4, 1}, + {0x0cc7, 0x0cc8, 1}, + {0x0cca, 0x0ccb, 1}, + {0x0cd5, 0x0cd6, 1}, + {0x0d02, 0x0d03, 1}, + {0x0d3e, 0x0d40, 1}, + {0x0d46, 0x0d48, 1}, + {0x0d4a, 0x0d4c, 1}, + {0x0d57, 0x0d82, 43}, + {0x0d83, 0x0dcf, 76}, + {0x0dd0, 0x0dd1, 1}, + {0x0dd8, 0x0ddf, 1}, + {0x0df2, 0x0df3, 1}, + {0x0f3e, 0x0f3f, 1}, + {0x0f7f, 0x102b, 172}, + {0x102c, 0x1031, 5}, + {0x1038, 0x103b, 3}, + {0x103c, 0x1056, 26}, + {0x1057, 0x1062, 11}, + {0x1063, 0x1064, 1}, + {0x1067, 0x106d, 1}, + {0x1083, 0x1084, 1}, + {0x1087, 0x108c, 1}, + {0x108f, 0x109a, 11}, + {0x109b, 0x109c, 1}, + {0x17b6, 0x17be, 8}, + {0x17bf, 0x17c5, 1}, + {0x17c7, 0x17c8, 1}, + {0x1923, 0x1926, 1}, + {0x1929, 0x192b, 1}, + {0x1930, 0x1931, 1}, + {0x1933, 0x1938, 1}, + {0x19b0, 0x19c0, 1}, + {0x19c8, 0x19c9, 1}, + {0x1a19, 0x1a1b, 1}, + {0x1a55, 0x1a57, 2}, + {0x1a61, 0x1a63, 2}, + {0x1a64, 0x1a6d, 9}, + {0x1a6e, 0x1a72, 1}, + {0x1b04, 0x1b35, 49}, + {0x1b3b, 0x1b3d, 2}, + {0x1b3e, 0x1b41, 1}, + {0x1b43, 0x1b44, 1}, + {0x1b82, 0x1ba1, 31}, + {0x1ba6, 0x1ba7, 1}, + {0x1baa, 0x1be7, 61}, + {0x1bea, 0x1bec, 1}, + {0x1bee, 0x1bf2, 4}, + {0x1bf3, 0x1c24, 49}, + {0x1c25, 0x1c2b, 1}, + {0x1c34, 0x1c35, 1}, + {0x1ce1, 0x1cf2, 17}, + {0xa823, 0xa824, 1}, + {0xa827, 0xa880, 89}, + {0xa881, 0xa8b4, 51}, + {0xa8b5, 0xa8c3, 1}, + {0xa952, 0xa953, 1}, + {0xa983, 0xa9b4, 49}, + {0xa9b5, 0xa9ba, 5}, + {0xa9bb, 0xa9bd, 2}, + {0xa9be, 0xa9c0, 1}, + {0xaa2f, 0xaa30, 1}, + {0xaa33, 0xaa34, 1}, + {0xaa4d, 0xaa7b, 46}, + {0xabe3, 0xabe4, 1}, + {0xabe6, 0xabe7, 1}, + {0xabe9, 0xabea, 1}, + {0xabec, 0xabec, 1}, + }, + R32: []Range32{ + {0x11000, 0x11000, 1}, + {0x11002, 0x11082, 128}, + {0x110b0, 0x110b2, 1}, + {0x110b7, 0x110b8, 1}, + {0x1d165, 0x1d166, 1}, + {0x1d16d, 0x1d172, 1}, + }, +} + +var _Mn = &RangeTable{ + R16: []Range16{ + {0x0300, 0x036f, 1}, + {0x0483, 0x0487, 1}, + {0x0591, 0x05bd, 1}, + {0x05bf, 0x05c1, 2}, + {0x05c2, 0x05c4, 2}, + {0x05c5, 0x05c7, 2}, + {0x0610, 0x061a, 1}, + {0x064b, 0x065f, 1}, + {0x0670, 0x06d6, 102}, + {0x06d7, 0x06dc, 1}, + {0x06df, 0x06e4, 1}, + {0x06e7, 0x06e8, 1}, + {0x06ea, 0x06ed, 1}, + {0x0711, 0x0730, 31}, + {0x0731, 0x074a, 1}, + {0x07a6, 0x07b0, 1}, + {0x07eb, 0x07f3, 1}, + {0x0816, 0x0819, 1}, + {0x081b, 0x0823, 1}, + {0x0825, 0x0827, 1}, + {0x0829, 0x082d, 1}, + {0x0859, 0x085b, 1}, + {0x0900, 0x0902, 1}, + {0x093a, 0x093c, 2}, + {0x0941, 0x0948, 1}, + {0x094d, 0x0951, 4}, + {0x0952, 0x0957, 1}, + {0x0962, 0x0963, 1}, + {0x0981, 0x09bc, 59}, + {0x09c1, 0x09c4, 1}, + {0x09cd, 0x09e2, 21}, + {0x09e3, 0x0a01, 30}, + {0x0a02, 0x0a3c, 58}, + {0x0a41, 0x0a42, 1}, + {0x0a47, 0x0a48, 1}, + {0x0a4b, 0x0a4d, 1}, + {0x0a51, 0x0a70, 31}, + {0x0a71, 0x0a75, 4}, + {0x0a81, 0x0a82, 1}, + {0x0abc, 0x0ac1, 5}, + {0x0ac2, 0x0ac5, 1}, + {0x0ac7, 0x0ac8, 1}, + {0x0acd, 0x0ae2, 21}, + {0x0ae3, 0x0b01, 30}, + {0x0b3c, 0x0b3f, 3}, + {0x0b41, 0x0b44, 1}, + {0x0b4d, 0x0b56, 9}, + {0x0b62, 0x0b63, 1}, + {0x0b82, 0x0bc0, 62}, + {0x0bcd, 0x0c3e, 113}, + {0x0c3f, 0x0c40, 1}, + {0x0c46, 0x0c48, 1}, + {0x0c4a, 0x0c4d, 1}, + {0x0c55, 0x0c56, 1}, + {0x0c62, 0x0c63, 1}, + {0x0cbc, 0x0cbf, 3}, + {0x0cc6, 0x0ccc, 6}, + {0x0ccd, 0x0ce2, 21}, + {0x0ce3, 0x0d41, 94}, + {0x0d42, 0x0d44, 1}, + {0x0d4d, 0x0d62, 21}, + {0x0d63, 0x0dca, 103}, + {0x0dd2, 0x0dd4, 1}, + {0x0dd6, 0x0e31, 91}, + {0x0e34, 0x0e3a, 1}, + {0x0e47, 0x0e4e, 1}, + {0x0eb1, 0x0eb4, 3}, + {0x0eb5, 0x0eb9, 1}, + {0x0ebb, 0x0ebc, 1}, + {0x0ec8, 0x0ecd, 1}, + {0x0f18, 0x0f19, 1}, + {0x0f35, 0x0f39, 2}, + {0x0f71, 0x0f7e, 1}, + {0x0f80, 0x0f84, 1}, + {0x0f86, 0x0f87, 1}, + {0x0f8d, 0x0f97, 1}, + {0x0f99, 0x0fbc, 1}, + {0x0fc6, 0x102d, 103}, + {0x102e, 0x1030, 1}, + {0x1032, 0x1037, 1}, + {0x1039, 0x103a, 1}, + {0x103d, 0x103e, 1}, + {0x1058, 0x1059, 1}, + {0x105e, 0x1060, 1}, + {0x1071, 0x1074, 1}, + {0x1082, 0x1085, 3}, + {0x1086, 0x108d, 7}, + {0x109d, 0x135d, 704}, + {0x135e, 0x135f, 1}, + {0x1712, 0x1714, 1}, + {0x1732, 0x1734, 1}, + {0x1752, 0x1753, 1}, + {0x1772, 0x1773, 1}, + {0x17b7, 0x17bd, 1}, + {0x17c6, 0x17c9, 3}, + {0x17ca, 0x17d3, 1}, + {0x17dd, 0x180b, 46}, + {0x180c, 0x180d, 1}, + {0x18a9, 0x1920, 119}, + {0x1921, 0x1922, 1}, + {0x1927, 0x1928, 1}, + {0x1932, 0x1939, 7}, + {0x193a, 0x193b, 1}, + {0x1a17, 0x1a18, 1}, + {0x1a56, 0x1a58, 2}, + {0x1a59, 0x1a5e, 1}, + {0x1a60, 0x1a62, 2}, + {0x1a65, 0x1a6c, 1}, + {0x1a73, 0x1a7c, 1}, + {0x1a7f, 0x1b00, 129}, + {0x1b01, 0x1b03, 1}, + {0x1b34, 0x1b36, 2}, + {0x1b37, 0x1b3a, 1}, + {0x1b3c, 0x1b42, 6}, + {0x1b6b, 0x1b73, 1}, + {0x1b80, 0x1b81, 1}, + {0x1ba2, 0x1ba5, 1}, + {0x1ba8, 0x1ba9, 1}, + {0x1be6, 0x1be8, 2}, + {0x1be9, 0x1bed, 4}, + {0x1bef, 0x1bf1, 1}, + {0x1c2c, 0x1c33, 1}, + {0x1c36, 0x1c37, 1}, + {0x1cd0, 0x1cd2, 1}, + {0x1cd4, 0x1ce0, 1}, + {0x1ce2, 0x1ce8, 1}, + {0x1ced, 0x1dc0, 211}, + {0x1dc1, 0x1de6, 1}, + {0x1dfc, 0x1dff, 1}, + {0x20d0, 0x20dc, 1}, + {0x20e1, 0x20e5, 4}, + {0x20e6, 0x20f0, 1}, + {0x2cef, 0x2cf1, 1}, + {0x2d7f, 0x2de0, 97}, + {0x2de1, 0x2dff, 1}, + {0x302a, 0x302f, 1}, + {0x3099, 0x309a, 1}, + {0xa66f, 0xa67c, 13}, + {0xa67d, 0xa6f0, 115}, + {0xa6f1, 0xa802, 273}, + {0xa806, 0xa80b, 5}, + {0xa825, 0xa826, 1}, + {0xa8c4, 0xa8e0, 28}, + {0xa8e1, 0xa8f1, 1}, + {0xa926, 0xa92d, 1}, + {0xa947, 0xa951, 1}, + {0xa980, 0xa982, 1}, + {0xa9b3, 0xa9b6, 3}, + {0xa9b7, 0xa9b9, 1}, + {0xa9bc, 0xaa29, 109}, + {0xaa2a, 0xaa2e, 1}, + {0xaa31, 0xaa32, 1}, + {0xaa35, 0xaa36, 1}, + {0xaa43, 0xaa4c, 9}, + {0xaab0, 0xaab2, 2}, + {0xaab3, 0xaab4, 1}, + {0xaab7, 0xaab8, 1}, + {0xaabe, 0xaabf, 1}, + {0xaac1, 0xabe5, 292}, + {0xabe8, 0xabed, 5}, + {0xfb1e, 0xfe00, 738}, + {0xfe01, 0xfe0f, 1}, + {0xfe20, 0xfe26, 1}, + }, + R32: []Range32{ + {0x101fd, 0x10a01, 2052}, + {0x10a02, 0x10a03, 1}, + {0x10a05, 0x10a06, 1}, + {0x10a0c, 0x10a0f, 1}, + {0x10a38, 0x10a3a, 1}, + {0x10a3f, 0x11001, 1474}, + {0x11038, 0x11046, 1}, + {0x11080, 0x11081, 1}, + {0x110b3, 0x110b6, 1}, + {0x110b9, 0x110ba, 1}, + {0x1d167, 0x1d169, 1}, + {0x1d17b, 0x1d182, 1}, + {0x1d185, 0x1d18b, 1}, + {0x1d1aa, 0x1d1ad, 1}, + {0x1d242, 0x1d244, 1}, + {0xe0100, 0xe01ef, 1}, + }, +} + +var _Zl = &RangeTable{ + R16: []Range16{ + {0x2028, 0x2028, 1}, + }, +} + var _Zp = &RangeTable{ R16: []Range16{ {0x2029, 0x2029, 1}, @@ -2068,40 +2702,53 @@ var _Lo = &RangeTable{ } var ( - Cc = _Cc // Cc is the set of Unicode characters in category Cc. - Cf = _Cf // Cf is the set of Unicode characters in category Cf. - Co = _Co // Co is the set of Unicode characters in category Co. - Cs = _Cs // Cs is the set of Unicode characters in category Cs. - Digit = _Nd // Digit is the set of Unicode characters with the "decimal digit" property. - Nd = _Nd // Nd is the set of Unicode characters in category Nd. - Letter = letter // Letter is the set of Unicode letters. - Lm = _Lm // Lm is the set of Unicode characters in category Lm. - Lo = _Lo // Lo is the set of Unicode characters in category Lo. - Lower = _Ll // Lower is the set of Unicode lower case letters. - Ll = _Ll // Ll is the set of Unicode characters in category Ll. - Mc = _Mc // Mc is the set of Unicode characters in category Mc. - Me = _Me // Me is the set of Unicode characters in category Me. - Mn = _Mn // Mn is the set of Unicode characters in category Mn. - Nl = _Nl // Nl is the set of Unicode characters in category Nl. - No = _No // No is the set of Unicode characters in category No. - Pc = _Pc // Pc is the set of Unicode characters in category Pc. - Pd = _Pd // Pd is the set of Unicode characters in category Pd. - Pe = _Pe // Pe is the set of Unicode characters in category Pe. - Pf = _Pf // Pf is the set of Unicode characters in category Pf. - Pi = _Pi // Pi is the set of Unicode characters in category Pi. - Po = _Po // Po is the set of Unicode characters in category Po. - Ps = _Ps // Ps is the set of Unicode characters in category Ps. - Sc = _Sc // Sc is the set of Unicode characters in category Sc. - Sk = _Sk // Sk is the set of Unicode characters in category Sk. - Sm = _Sm // Sm is the set of Unicode characters in category Sm. - So = _So // So is the set of Unicode characters in category So. - Title = _Lt // Title is the set of Unicode title case letters. - Lt = _Lt // Lt is the set of Unicode characters in category Lt. - Upper = _Lu // Upper is the set of Unicode upper case letters. - Lu = _Lu // Lu is the set of Unicode characters in category Lu. - Zl = _Zl // Zl is the set of Unicode characters in category Zl. - Zp = _Zp // Zp is the set of Unicode characters in category Zp. - Zs = _Zs // Zs is the set of Unicode characters in category Zs. + Cc = _Cc // Cc is the set of Unicode characters in category Cc. + Cf = _Cf // Cf is the set of Unicode characters in category Cf. + Co = _Co // Co is the set of Unicode characters in category Co. + Cs = _Cs // Cs is the set of Unicode characters in category Cs. + Digit = _Nd // Digit is the set of Unicode characters with the "decimal digit" property. + Nd = _Nd // Nd is the set of Unicode characters in category Nd. + Letter = _L // Letter/L is the set of Unicode letters, category L. + L = _L + Lm = _Lm // Lm is the set of Unicode characters in category Lm. + Lo = _Lo // Lo is the set of Unicode characters in category Lo. + Lower = _Ll // Lower is the set of Unicode lower case letters. + Ll = _Ll // Ll is the set of Unicode characters in category Ll. + Mark = _M // Mark/M is the set of Unicode mark characters, category M. + M = _M + Mc = _Mc // Mc is the set of Unicode characters in category Mc. + Me = _Me // Me is the set of Unicode characters in category Me. + Mn = _Mn // Mn is the set of Unicode characters in category Mn. + Nl = _Nl // Nl is the set of Unicode characters in category Nl. + No = _No // No is the set of Unicode characters in category No. + Number = _N // Number/N is the set of Unicode number characters, category N. + N = _N + Other = _C // Other/C is the set of Unicode control and special characters, category C. + C = _C + Pc = _Pc // Pc is the set of Unicode characters in category Pc. + Pd = _Pd // Pd is the set of Unicode characters in category Pd. + Pe = _Pe // Pe is the set of Unicode characters in category Pe. + Pf = _Pf // Pf is the set of Unicode characters in category Pf. + Pi = _Pi // Pi is the set of Unicode characters in category Pi. + Po = _Po // Po is the set of Unicode characters in category Po. + Ps = _Ps // Ps is the set of Unicode characters in category Ps. + Punct = _P // Punct/P is the set of Unicode punctuation characters, category P. + P = _P + Sc = _Sc // Sc is the set of Unicode characters in category Sc. + Sk = _Sk // Sk is the set of Unicode characters in category Sk. + Sm = _Sm // Sm is the set of Unicode characters in category Sm. + So = _So // So is the set of Unicode characters in category So. + Space = _Z // Space/Z is the set of Unicode space characters, category Z. + Z = _Z + Symbol = _S // Symbol/S is the set of Unicode symbol characters, category S. + S = _S + Title = _Lt // Title is the set of Unicode title case letters. + Lt = _Lt // Lt is the set of Unicode characters in category Lt. + Upper = _Lu // Upper is the set of Unicode upper case letters. + Lu = _Lu // Lu is the set of Unicode characters in category Lu. + Zl = _Zl // Zl is the set of Unicode characters in category Zl. + Zp = _Zp // Zp is the set of Unicode characters in category Zp. + Zs = _Zs // Zs is the set of Unicode characters in category Zs. ) // Generated by running @@ -4764,6 +5411,264 @@ var _CaseRanges = []CaseRange{ {0x10400, 0x10427, d{0, 40, 0}}, {0x10428, 0x1044F, d{-40, 0, -40}}, } - -// Range entries: 2715 16-bit, 545 32-bit, 3260 total. -// Range bytes: 16290 16-bit, 6540 32-bit, 22830 total. +var properties = [MaxLatin1 + 1]uint8{ + 0x00: pC, // '\x00' + 0x01: pC, // '\x01' + 0x02: pC, // '\x02' + 0x03: pC, // '\x03' + 0x04: pC, // '\x04' + 0x05: pC, // '\x05' + 0x06: pC, // '\x06' + 0x07: pC, // '\a' + 0x08: pC, // '\b' + 0x09: pC, // '\t' + 0x0A: pC, // '\n' + 0x0B: pC, // '\v' + 0x0C: pC, // '\f' + 0x0D: pC, // '\r' + 0x0E: pC, // '\x0e' + 0x0F: pC, // '\x0f' + 0x10: pC, // '\x10' + 0x11: pC, // '\x11' + 0x12: pC, // '\x12' + 0x13: pC, // '\x13' + 0x14: pC, // '\x14' + 0x15: pC, // '\x15' + 0x16: pC, // '\x16' + 0x17: pC, // '\x17' + 0x18: pC, // '\x18' + 0x19: pC, // '\x19' + 0x1A: pC, // '\x1a' + 0x1B: pC, // '\x1b' + 0x1C: pC, // '\x1c' + 0x1D: pC, // '\x1d' + 0x1E: pC, // '\x1e' + 0x1F: pC, // '\x1f' + 0x20: pZ | pp, // ' ' + 0x21: pP | pp, // '!' + 0x22: pP | pp, // '"' + 0x23: pP | pp, // '#' + 0x24: pS | pp, // '$' + 0x25: pP | pp, // '%' + 0x26: pP | pp, // '&' + 0x27: pP | pp, // '\'' + 0x28: pP | pp, // '(' + 0x29: pP | pp, // ')' + 0x2A: pP | pp, // '*' + 0x2B: pS | pp, // '+' + 0x2C: pP | pp, // ',' + 0x2D: pP | pp, // '-' + 0x2E: pP | pp, // '.' + 0x2F: pP | pp, // '/' + 0x30: pN | pp, // '0' + 0x31: pN | pp, // '1' + 0x32: pN | pp, // '2' + 0x33: pN | pp, // '3' + 0x34: pN | pp, // '4' + 0x35: pN | pp, // '5' + 0x36: pN | pp, // '6' + 0x37: pN | pp, // '7' + 0x38: pN | pp, // '8' + 0x39: pN | pp, // '9' + 0x3A: pP | pp, // ':' + 0x3B: pP | pp, // ';' + 0x3C: pS | pp, // '<' + 0x3D: pS | pp, // '=' + 0x3E: pS | pp, // '>' + 0x3F: pP | pp, // '?' + 0x40: pP | pp, // '@' + 0x41: pLu | pp, // 'A' + 0x42: pLu | pp, // 'B' + 0x43: pLu | pp, // 'C' + 0x44: pLu | pp, // 'D' + 0x45: pLu | pp, // 'E' + 0x46: pLu | pp, // 'F' + 0x47: pLu | pp, // 'G' + 0x48: pLu | pp, // 'H' + 0x49: pLu | pp, // 'I' + 0x4A: pLu | pp, // 'J' + 0x4B: pLu | pp, // 'K' + 0x4C: pLu | pp, // 'L' + 0x4D: pLu | pp, // 'M' + 0x4E: pLu | pp, // 'N' + 0x4F: pLu | pp, // 'O' + 0x50: pLu | pp, // 'P' + 0x51: pLu | pp, // 'Q' + 0x52: pLu | pp, // 'R' + 0x53: pLu | pp, // 'S' + 0x54: pLu | pp, // 'T' + 0x55: pLu | pp, // 'U' + 0x56: pLu | pp, // 'V' + 0x57: pLu | pp, // 'W' + 0x58: pLu | pp, // 'X' + 0x59: pLu | pp, // 'Y' + 0x5A: pLu | pp, // 'Z' + 0x5B: pP | pp, // '[' + 0x5C: pP | pp, // '\\' + 0x5D: pP | pp, // ']' + 0x5E: pS | pp, // '^' + 0x5F: pP | pp, // '_' + 0x60: pS | pp, // '`' + 0x61: pLl | pp, // 'a' + 0x62: pLl | pp, // 'b' + 0x63: pLl | pp, // 'c' + 0x64: pLl | pp, // 'd' + 0x65: pLl | pp, // 'e' + 0x66: pLl | pp, // 'f' + 0x67: pLl | pp, // 'g' + 0x68: pLl | pp, // 'h' + 0x69: pLl | pp, // 'i' + 0x6A: pLl | pp, // 'j' + 0x6B: pLl | pp, // 'k' + 0x6C: pLl | pp, // 'l' + 0x6D: pLl | pp, // 'm' + 0x6E: pLl | pp, // 'n' + 0x6F: pLl | pp, // 'o' + 0x70: pLl | pp, // 'p' + 0x71: pLl | pp, // 'q' + 0x72: pLl | pp, // 'r' + 0x73: pLl | pp, // 's' + 0x74: pLl | pp, // 't' + 0x75: pLl | pp, // 'u' + 0x76: pLl | pp, // 'v' + 0x77: pLl | pp, // 'w' + 0x78: pLl | pp, // 'x' + 0x79: pLl | pp, // 'y' + 0x7A: pLl | pp, // 'z' + 0x7B: pP | pp, // '{' + 0x7C: pS | pp, // '|' + 0x7D: pP | pp, // '}' + 0x7E: pS | pp, // '~' + 0x7F: pC, // '\x7f' + 0x80: pC, // '\u0080' + 0x81: pC, // '\u0081' + 0x82: pC, // '\u0082' + 0x83: pC, // '\u0083' + 0x84: pC, // '\u0084' + 0x85: pC, // '\u0085' + 0x86: pC, // '\u0086' + 0x87: pC, // '\u0087' + 0x88: pC, // '\u0088' + 0x89: pC, // '\u0089' + 0x8A: pC, // '\u008a' + 0x8B: pC, // '\u008b' + 0x8C: pC, // '\u008c' + 0x8D: pC, // '\u008d' + 0x8E: pC, // '\u008e' + 0x8F: pC, // '\u008f' + 0x90: pC, // '\u0090' + 0x91: pC, // '\u0091' + 0x92: pC, // '\u0092' + 0x93: pC, // '\u0093' + 0x94: pC, // '\u0094' + 0x95: pC, // '\u0095' + 0x96: pC, // '\u0096' + 0x97: pC, // '\u0097' + 0x98: pC, // '\u0098' + 0x99: pC, // '\u0099' + 0x9A: pC, // '\u009a' + 0x9B: pC, // '\u009b' + 0x9C: pC, // '\u009c' + 0x9D: pC, // '\u009d' + 0x9E: pC, // '\u009e' + 0x9F: pC, // '\u009f' + 0xA0: pZ, // '\u00a0' + 0xA1: pP | pp, // '\u00a1' + 0xA2: pS | pp, // '\u00a2' + 0xA3: pS | pp, // '\u00a3' + 0xA4: pS | pp, // '\u00a4' + 0xA5: pS | pp, // '\u00a5' + 0xA6: pS | pp, // '\u00a6' + 0xA7: pS | pp, // '\u00a7' + 0xA8: pS | pp, // '\u00a8' + 0xA9: pS | pp, // '\u00a9' + 0xAA: pLl | pp, // '\u00aa' + 0xAB: pP | pp, // '\u00ab' + 0xAC: pS | pp, // '\u00ac' + 0xAD: 0, // '\u00ad' + 0xAE: pS | pp, // '\u00ae' + 0xAF: pS | pp, // '\u00af' + 0xB0: pS | pp, // '\u00b0' + 0xB1: pS | pp, // '\u00b1' + 0xB2: pN | pp, // '\u00b2' + 0xB3: pN | pp, // '\u00b3' + 0xB4: pS | pp, // '\u00b4' + 0xB5: pLl | pp, // '\u00b5' + 0xB6: pS | pp, // '\u00b6' + 0xB7: pP | pp, // '\u00b7' + 0xB8: pS | pp, // '\u00b8' + 0xB9: pN | pp, // '\u00b9' + 0xBA: pLl | pp, // '\u00ba' + 0xBB: pP | pp, // '\u00bb' + 0xBC: pN | pp, // '\u00bc' + 0xBD: pN | pp, // '\u00bd' + 0xBE: pN | pp, // '\u00be' + 0xBF: pP | pp, // '\u00bf' + 0xC0: pLu | pp, // '\u00c0' + 0xC1: pLu | pp, // '\u00c1' + 0xC2: pLu | pp, // '\u00c2' + 0xC3: pLu | pp, // '\u00c3' + 0xC4: pLu | pp, // '\u00c4' + 0xC5: pLu | pp, // '\u00c5' + 0xC6: pLu | pp, // '\u00c6' + 0xC7: pLu | pp, // '\u00c7' + 0xC8: pLu | pp, // '\u00c8' + 0xC9: pLu | pp, // '\u00c9' + 0xCA: pLu | pp, // '\u00ca' + 0xCB: pLu | pp, // '\u00cb' + 0xCC: pLu | pp, // '\u00cc' + 0xCD: pLu | pp, // '\u00cd' + 0xCE: pLu | pp, // '\u00ce' + 0xCF: pLu | pp, // '\u00cf' + 0xD0: pLu | pp, // '\u00d0' + 0xD1: pLu | pp, // '\u00d1' + 0xD2: pLu | pp, // '\u00d2' + 0xD3: pLu | pp, // '\u00d3' + 0xD4: pLu | pp, // '\u00d4' + 0xD5: pLu | pp, // '\u00d5' + 0xD6: pLu | pp, // '\u00d6' + 0xD7: pS | pp, // '\u00d7' + 0xD8: pLu | pp, // '\u00d8' + 0xD9: pLu | pp, // '\u00d9' + 0xDA: pLu | pp, // '\u00da' + 0xDB: pLu | pp, // '\u00db' + 0xDC: pLu | pp, // '\u00dc' + 0xDD: pLu | pp, // '\u00dd' + 0xDE: pLu | pp, // '\u00de' + 0xDF: pLl | pp, // '\u00df' + 0xE0: pLl | pp, // '\u00e0' + 0xE1: pLl | pp, // '\u00e1' + 0xE2: pLl | pp, // '\u00e2' + 0xE3: pLl | pp, // '\u00e3' + 0xE4: pLl | pp, // '\u00e4' + 0xE5: pLl | pp, // '\u00e5' + 0xE6: pLl | pp, // '\u00e6' + 0xE7: pLl | pp, // '\u00e7' + 0xE8: pLl | pp, // '\u00e8' + 0xE9: pLl | pp, // '\u00e9' + 0xEA: pLl | pp, // '\u00ea' + 0xEB: pLl | pp, // '\u00eb' + 0xEC: pLl | pp, // '\u00ec' + 0xED: pLl | pp, // '\u00ed' + 0xEE: pLl | pp, // '\u00ee' + 0xEF: pLl | pp, // '\u00ef' + 0xF0: pLl | pp, // '\u00f0' + 0xF1: pLl | pp, // '\u00f1' + 0xF2: pLl | pp, // '\u00f2' + 0xF3: pLl | pp, // '\u00f3' + 0xF4: pLl | pp, // '\u00f4' + 0xF5: pLl | pp, // '\u00f5' + 0xF6: pLl | pp, // '\u00f6' + 0xF7: pS | pp, // '\u00f7' + 0xF8: pLl | pp, // '\u00f8' + 0xF9: pLl | pp, // '\u00f9' + 0xFA: pLl | pp, // '\u00fa' + 0xFB: pLl | pp, // '\u00fb' + 0xFC: pLl | pp, // '\u00fc' + 0xFD: pLl | pp, // '\u00fd' + 0xFE: pLl | pp, // '\u00fe' + 0xFF: pLl | pp, // '\u00ff' +} + +// Range entries: 3190 16-bit, 657 32-bit, 3847 total. +// Range bytes: 19140 16-bit, 7884 32-bit, 27024 total. |