github.com/rohankumardubey/syslog-redirector-golang@v0.0.0-20140320174030-4859f03d829a/src/cmd/5l/asm.c (about) 1 // Inferno utils/5l/asm.c 2 // http://code.google.com/p/inferno-os/source/browse/utils/5l/asm.c 3 // 4 // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. 5 // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) 6 // Portions Copyright © 1997-1999 Vita Nuova Limited 7 // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) 8 // Portions Copyright © 2004,2006 Bruce Ellis 9 // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) 10 // Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others 11 // Portions Copyright © 2009 The Go Authors. All rights reserved. 12 // 13 // Permission is hereby granted, free of charge, to any person obtaining a copy 14 // of this software and associated documentation files (the "Software"), to deal 15 // in the Software without restriction, including without limitation the rights 16 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 // copies of the Software, and to permit persons to whom the Software is 18 // furnished to do so, subject to the following conditions: 19 // 20 // The above copyright notice and this permission notice shall be included in 21 // all copies or substantial portions of the Software. 22 // 23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 29 // THE SOFTWARE. 30 31 // Writing object files. 32 33 #include "l.h" 34 #include "../ld/lib.h" 35 #include "../ld/elf.h" 36 #include "../ld/dwarf.h" 37 38 static Prog *PP; 39 40 char linuxdynld[] = "/lib/ld-linux.so.3"; // 2 for OABI, 3 for EABI 41 char freebsddynld[] = "/usr/libexec/ld-elf.so.1"; 42 char openbsddynld[] = "XXX"; 43 char netbsddynld[] = "/libexec/ld.elf_so"; 44 char dragonflydynld[] = "XXX"; 45 46 int32 47 entryvalue(void) 48 { 49 char *a; 50 Sym *s; 51 52 a = INITENTRY; 53 if(*a >= '0' && *a <= '9') 54 return atolwhex(a); 55 s = lookup(a, 0); 56 if(s->type == 0) 57 return INITTEXT; 58 if(s->type != STEXT) 59 diag("entry not text: %s", s->name); 60 return s->value; 61 } 62 63 static int 64 needlib(char *name) 65 { 66 char *p; 67 Sym *s; 68 69 if(*name == '\0') 70 return 0; 71 72 /* reuse hash code in symbol table */ 73 p = smprint(".dynlib.%s", name); 74 s = lookup(p, 0); 75 free(p); 76 if(s->type == 0) { 77 s->type = 100; // avoid SDATA, etc. 78 return 1; 79 } 80 return 0; 81 } 82 83 int nelfsym = 1; 84 85 static void addpltsym(Sym*); 86 static void addgotsym(Sym*); 87 static void addgotsyminternal(Sym*); 88 89 // Preserve highest 8 bits of a, and do addition to lower 24-bit 90 // of a and b; used to adjust ARM branch intruction's target 91 static int32 92 braddoff(int32 a, int32 b) 93 { 94 return (((uint32)a) & 0xff000000U) | (0x00ffffffU & (uint32)(a + b)); 95 } 96 97 void 98 adddynrela(Sym *rel, Sym *s, Reloc *r) 99 { 100 addaddrplus(rel, s, r->off); 101 adduint32(rel, R_ARM_RELATIVE); 102 } 103 104 void 105 adddynrel(Sym *s, Reloc *r) 106 { 107 Sym *targ, *rel; 108 109 targ = r->sym; 110 cursym = s; 111 112 switch(r->type) { 113 default: 114 if(r->type >= 256) { 115 diag("unexpected relocation type %d", r->type); 116 return; 117 } 118 break; 119 120 // Handle relocations found in ELF object files. 121 case 256 + R_ARM_PLT32: 122 r->type = D_CALL; 123 if(targ->type == SDYNIMPORT) { 124 addpltsym(targ); 125 r->sym = lookup(".plt", 0); 126 r->add = braddoff(r->add, targ->plt / 4); 127 } 128 return; 129 130 case 256 + R_ARM_THM_PC22: // R_ARM_THM_CALL 131 diag("R_ARM_THM_CALL, are you using -marm?"); 132 errorexit(); 133 return; 134 135 case 256 + R_ARM_GOT32: // R_ARM_GOT_BREL 136 if(targ->type != SDYNIMPORT) { 137 addgotsyminternal(targ); 138 } else { 139 addgotsym(targ); 140 } 141 r->type = D_CONST; // write r->add during relocsym 142 r->sym = S; 143 r->add += targ->got; 144 return; 145 146 case 256 + R_ARM_GOT_PREL: // GOT(S) + A - P 147 if(targ->type != SDYNIMPORT) { 148 addgotsyminternal(targ); 149 } else { 150 addgotsym(targ); 151 } 152 r->type = D_PCREL; 153 r->sym = lookup(".got", 0); 154 r->add += targ->got + 4; 155 return; 156 157 case 256 + R_ARM_GOTOFF: // R_ARM_GOTOFF32 158 r->type = D_GOTOFF; 159 return; 160 161 case 256 + R_ARM_GOTPC: // R_ARM_BASE_PREL 162 r->type = D_PCREL; 163 r->sym = lookup(".got", 0); 164 r->add += 4; 165 return; 166 167 case 256 + R_ARM_CALL: 168 r->type = D_CALL; 169 if(targ->type == SDYNIMPORT) { 170 addpltsym(targ); 171 r->sym = lookup(".plt", 0); 172 r->add = braddoff(r->add, targ->plt / 4); 173 } 174 return; 175 176 case 256 + R_ARM_REL32: // R_ARM_REL32 177 r->type = D_PCREL; 178 r->add += 4; 179 return; 180 181 case 256 + R_ARM_ABS32: 182 if(targ->type == SDYNIMPORT) 183 diag("unexpected R_ARM_ABS32 relocation for dynamic symbol %s", targ->name); 184 r->type = D_ADDR; 185 return; 186 187 case 256 + R_ARM_V4BX: 188 // we can just ignore this, because we are targeting ARM V5+ anyway 189 if(r->sym) { 190 // R_ARM_V4BX is ABS relocation, so this symbol is a dummy symbol, ignore it 191 r->sym->type = 0; 192 } 193 r->sym = S; 194 return; 195 196 case 256 + R_ARM_PC24: 197 case 256 + R_ARM_JUMP24: 198 r->type = D_CALL; 199 if(targ->type == SDYNIMPORT) { 200 addpltsym(targ); 201 r->sym = lookup(".plt", 0); 202 r->add = braddoff(r->add, targ->plt / 4); 203 } 204 return; 205 } 206 207 // Handle references to ELF symbols from our own object files. 208 if(targ->type != SDYNIMPORT) 209 return; 210 211 switch(r->type) { 212 case D_PCREL: 213 addpltsym(targ); 214 r->sym = lookup(".plt", 0); 215 r->add = targ->plt; 216 return; 217 218 case D_ADDR: 219 if(s->type != SDATA) 220 break; 221 if(iself) { 222 adddynsym(targ); 223 rel = lookup(".rel", 0); 224 addaddrplus(rel, s, r->off); 225 adduint32(rel, ELF32_R_INFO(targ->dynid, R_ARM_GLOB_DAT)); // we need a S + A dynmic reloc 226 r->type = D_CONST; // write r->add during relocsym 227 r->sym = S; 228 return; 229 } 230 break; 231 } 232 233 cursym = s; 234 diag("unsupported relocation for dynamic symbol %s (type=%d stype=%d)", targ->name, r->type, targ->type); 235 } 236 237 int 238 elfreloc1(Reloc *r, vlong sectoff) 239 { 240 int32 elfsym; 241 242 LPUT(sectoff); 243 244 elfsym = r->xsym->elfsym; 245 switch(r->type) { 246 default: 247 return -1; 248 249 case D_ADDR: 250 if(r->siz == 4) 251 LPUT(R_ARM_ABS32 | elfsym<<8); 252 else 253 return -1; 254 break; 255 256 case D_PCREL: 257 if(r->siz == 4) 258 LPUT(R_ARM_REL32 | elfsym<<8); 259 else 260 return -1; 261 break; 262 263 case D_CALL: 264 if(r->siz == 4) { 265 if((r->add & 0xff000000) == 0xeb000000) // BL 266 LPUT(R_ARM_CALL | elfsym<<8); 267 else 268 LPUT(R_ARM_JUMP24 | elfsym<<8); 269 } else 270 return -1; 271 break; 272 273 case D_TLS: 274 if(r->siz == 4) { 275 if(flag_shared) 276 LPUT(R_ARM_TLS_IE32 | elfsym<<8); 277 else 278 LPUT(R_ARM_TLS_LE32 | elfsym<<8); 279 } else 280 return -1; 281 break; 282 } 283 284 return 0; 285 } 286 287 void 288 elfsetupplt(void) 289 { 290 Sym *plt, *got; 291 292 plt = lookup(".plt", 0); 293 got = lookup(".got.plt", 0); 294 if(plt->size == 0) { 295 // str lr, [sp, #-4]! 296 adduint32(plt, 0xe52de004); 297 // ldr lr, [pc, #4] 298 adduint32(plt, 0xe59fe004); 299 // add lr, pc, lr 300 adduint32(plt, 0xe08fe00e); 301 // ldr pc, [lr, #8]! 302 adduint32(plt, 0xe5bef008); 303 // .word &GLOBAL_OFFSET_TABLE[0] - . 304 addpcrelplus(plt, got, 4); 305 306 // the first .plt entry requires 3 .plt.got entries 307 adduint32(got, 0); 308 adduint32(got, 0); 309 adduint32(got, 0); 310 } 311 } 312 313 int 314 machoreloc1(Reloc *r, vlong sectoff) 315 { 316 USED(r); 317 USED(sectoff); 318 319 return -1; 320 } 321 322 323 int 324 archreloc(Reloc *r, Sym *s, vlong *val) 325 { 326 Sym *rs; 327 328 if(linkmode == LinkExternal) { 329 switch(r->type) { 330 case D_CALL: 331 r->done = 0; 332 333 // set up addend for eventual relocation via outer symbol. 334 rs = r->sym; 335 r->xadd = r->add; 336 if(r->xadd & 0x800000) 337 r->xadd |= ~0xffffff; 338 r->xadd *= 4; 339 while(rs->outer != nil) { 340 r->xadd += symaddr(rs) - symaddr(rs->outer); 341 rs = rs->outer; 342 } 343 344 if(rs->type != SHOSTOBJ && rs->sect == nil) 345 diag("missing section for %s", rs->name); 346 r->xsym = rs; 347 348 *val = braddoff((0xff000000U & (uint32)r->add), 349 (0xffffff & (uint32)(r->xadd / 4))); 350 return 0; 351 } 352 return -1; 353 } 354 switch(r->type) { 355 case D_CONST: 356 *val = r->add; 357 return 0; 358 case D_GOTOFF: 359 *val = symaddr(r->sym) + r->add - symaddr(lookup(".got", 0)); 360 return 0; 361 // The following three arch specific relocations are only for generation of 362 // Linux/ARM ELF's PLT entry (3 assembler instruction) 363 case D_PLT0: // add ip, pc, #0xXX00000 364 if (symaddr(lookup(".got.plt", 0)) < symaddr(lookup(".plt", 0))) 365 diag(".got.plt should be placed after .plt section."); 366 *val = 0xe28fc600U + 367 (0xff & ((uint32)(symaddr(r->sym) - (symaddr(lookup(".plt", 0)) + r->off) + r->add) >> 20)); 368 return 0; 369 case D_PLT1: // add ip, ip, #0xYY000 370 *val = 0xe28cca00U + 371 (0xff & ((uint32)(symaddr(r->sym) - (symaddr(lookup(".plt", 0)) + r->off) + r->add + 4) >> 12)); 372 return 0; 373 case D_PLT2: // ldr pc, [ip, #0xZZZ]! 374 *val = 0xe5bcf000U + 375 (0xfff & (uint32)(symaddr(r->sym) - (symaddr(lookup(".plt", 0)) + r->off) + r->add + 8)); 376 return 0; 377 case D_CALL: // bl XXXXXX or b YYYYYY 378 *val = braddoff((0xff000000U & (uint32)r->add), 379 (0xffffff & (uint32) 380 ((symaddr(r->sym) + ((uint32)r->add) * 4 - (s->value + r->off)) / 4))); 381 return 0; 382 } 383 return -1; 384 } 385 386 static Reloc * 387 addpltreloc(Sym *plt, Sym *got, Sym *sym, int typ) 388 { 389 Reloc *r; 390 391 r = addrel(plt); 392 r->sym = got; 393 r->off = plt->size; 394 r->siz = 4; 395 r->type = typ; 396 r->add = sym->got - 8; 397 398 plt->reachable = 1; 399 plt->size += 4; 400 symgrow(plt, plt->size); 401 402 return r; 403 } 404 405 static void 406 addpltsym(Sym *s) 407 { 408 Sym *plt, *got, *rel; 409 410 if(s->plt >= 0) 411 return; 412 413 adddynsym(s); 414 415 if(iself) { 416 plt = lookup(".plt", 0); 417 got = lookup(".got.plt", 0); 418 rel = lookup(".rel.plt", 0); 419 if(plt->size == 0) 420 elfsetupplt(); 421 422 // .got entry 423 s->got = got->size; 424 // In theory, all GOT should point to the first PLT entry, 425 // Linux/ARM's dynamic linker will do that for us, but FreeBSD/ARM's 426 // dynamic linker won't, so we'd better do it ourselves. 427 addaddrplus(got, plt, 0); 428 429 // .plt entry, this depends on the .got entry 430 s->plt = plt->size; 431 addpltreloc(plt, got, s, D_PLT0); // add lr, pc, #0xXX00000 432 addpltreloc(plt, got, s, D_PLT1); // add lr, lr, #0xYY000 433 addpltreloc(plt, got, s, D_PLT2); // ldr pc, [lr, #0xZZZ]! 434 435 // rel 436 addaddrplus(rel, got, s->got); 437 adduint32(rel, ELF32_R_INFO(s->dynid, R_ARM_JUMP_SLOT)); 438 } else { 439 diag("addpltsym: unsupported binary format"); 440 } 441 } 442 443 static void 444 addgotsyminternal(Sym *s) 445 { 446 Sym *got; 447 448 if(s->got >= 0) 449 return; 450 451 got = lookup(".got", 0); 452 s->got = got->size; 453 454 addaddrplus(got, s, 0); 455 456 if(iself) { 457 ; 458 } else { 459 diag("addgotsyminternal: unsupported binary format"); 460 } 461 } 462 463 static void 464 addgotsym(Sym *s) 465 { 466 Sym *got, *rel; 467 468 if(s->got >= 0) 469 return; 470 471 adddynsym(s); 472 got = lookup(".got", 0); 473 s->got = got->size; 474 adduint32(got, 0); 475 476 if(iself) { 477 rel = lookup(".rel", 0); 478 addaddrplus(rel, got, s->got); 479 adduint32(rel, ELF32_R_INFO(s->dynid, R_ARM_GLOB_DAT)); 480 } else { 481 diag("addgotsym: unsupported binary format"); 482 } 483 } 484 485 void 486 adddynsym(Sym *s) 487 { 488 Sym *d; 489 int t; 490 char *name; 491 492 if(s->dynid >= 0) 493 return; 494 495 if(iself) { 496 s->dynid = nelfsym++; 497 498 d = lookup(".dynsym", 0); 499 500 /* name */ 501 name = s->extname; 502 adduint32(d, addstring(lookup(".dynstr", 0), name)); 503 504 /* value */ 505 if(s->type == SDYNIMPORT) 506 adduint32(d, 0); 507 else 508 addaddr(d, s); 509 510 /* size */ 511 adduint32(d, 0); 512 513 /* type */ 514 t = STB_GLOBAL << 4; 515 if((s->cgoexport & CgoExportDynamic) && (s->type&SMASK) == STEXT) 516 t |= STT_FUNC; 517 else 518 t |= STT_OBJECT; 519 adduint8(d, t); 520 adduint8(d, 0); 521 522 /* shndx */ 523 if(s->type == SDYNIMPORT) 524 adduint16(d, SHN_UNDEF); 525 else { 526 switch(s->type) { 527 default: 528 case STEXT: 529 t = 11; 530 break; 531 case SRODATA: 532 t = 12; 533 break; 534 case SDATA: 535 t = 13; 536 break; 537 case SBSS: 538 t = 14; 539 break; 540 } 541 adduint16(d, t); 542 } 543 } else { 544 diag("adddynsym: unsupported binary format"); 545 } 546 } 547 548 void 549 adddynlib(char *lib) 550 { 551 Sym *s; 552 553 if(!needlib(lib)) 554 return; 555 556 if(iself) { 557 s = lookup(".dynstr", 0); 558 if(s->size == 0) 559 addstring(s, ""); 560 elfwritedynent(lookup(".dynamic", 0), DT_NEEDED, addstring(s, lib)); 561 } else { 562 diag("adddynlib: unsupported binary format"); 563 } 564 } 565 566 vlong 567 datoff(vlong addr) 568 { 569 if(addr >= segdata.vaddr) 570 return addr - segdata.vaddr + segdata.fileoff; 571 if(addr >= segtext.vaddr) 572 return addr - segtext.vaddr + segtext.fileoff; 573 diag("datoff %#x", addr); 574 return 0; 575 } 576 577 void 578 asmb(void) 579 { 580 int32 t; 581 uint32 symo; 582 Section *sect; 583 Sym *sym; 584 int i; 585 586 if(debug['v']) 587 Bprint(&bso, "%5.2f asmb\n", cputime()); 588 Bflush(&bso); 589 590 if(iself) 591 asmbelfsetup(); 592 593 sect = segtext.sect; 594 cseek(sect->vaddr - segtext.vaddr + segtext.fileoff); 595 codeblk(sect->vaddr, sect->len); 596 for(sect = sect->next; sect != nil; sect = sect->next) { 597 cseek(sect->vaddr - segtext.vaddr + segtext.fileoff); 598 datblk(sect->vaddr, sect->len); 599 } 600 601 if(segrodata.filelen > 0) { 602 if(debug['v']) 603 Bprint(&bso, "%5.2f rodatblk\n", cputime()); 604 Bflush(&bso); 605 606 cseek(segrodata.fileoff); 607 datblk(segrodata.vaddr, segrodata.filelen); 608 } 609 610 if(debug['v']) 611 Bprint(&bso, "%5.2f datblk\n", cputime()); 612 Bflush(&bso); 613 614 cseek(segdata.fileoff); 615 datblk(segdata.vaddr, segdata.filelen); 616 617 /* output symbol table */ 618 symsize = 0; 619 lcsize = 0; 620 symo = 0; 621 if(!debug['s']) { 622 // TODO: rationalize 623 if(debug['v']) 624 Bprint(&bso, "%5.2f sym\n", cputime()); 625 Bflush(&bso); 626 switch(HEADTYPE) { 627 default: 628 if(iself) 629 goto ElfSym; 630 case Hnoheader: 631 case Hrisc: 632 case Hixp1200: 633 case Hipaq: 634 debug['s'] = 1; 635 break; 636 case Hplan9x32: 637 symo = HEADR+segtext.len+segdata.filelen; 638 break; 639 ElfSym: 640 symo = rnd(HEADR+segtext.filelen, INITRND)+rnd(HEADR+segrodata.filelen, INITRND)+segdata.filelen; 641 symo = rnd(symo, INITRND); 642 break; 643 } 644 cseek(symo); 645 switch(HEADTYPE) { 646 default: 647 if(iself) { 648 if(debug['v']) 649 Bprint(&bso, "%5.2f elfsym\n", cputime()); 650 asmelfsym(); 651 cflush(); 652 cwrite(elfstrdat, elfstrsize); 653 654 if(debug['v']) 655 Bprint(&bso, "%5.2f dwarf\n", cputime()); 656 dwarfemitdebugsections(); 657 658 if(linkmode == LinkExternal) 659 elfemitreloc(); 660 } 661 break; 662 case Hplan9x32: 663 asmplan9sym(); 664 cflush(); 665 666 sym = lookup("pclntab", 0); 667 if(sym != nil) { 668 lcsize = sym->np; 669 for(i=0; i < lcsize; i++) 670 cput(sym->p[i]); 671 672 cflush(); 673 } 674 break; 675 } 676 } 677 678 cursym = nil; 679 if(debug['v']) 680 Bprint(&bso, "%5.2f header\n", cputime()); 681 Bflush(&bso); 682 cseek(0L); 683 switch(HEADTYPE) { 684 default: 685 case Hnoheader: /* no header */ 686 break; 687 case Hrisc: /* aif for risc os */ 688 lputl(0xe1a00000); /* NOP - decompress code */ 689 lputl(0xe1a00000); /* NOP - relocation code */ 690 lputl(0xeb000000 + 12); /* BL - zero init code */ 691 lputl(0xeb000000 + 692 (entryvalue() 693 - INITTEXT 694 + HEADR 695 - 12 696 - 8) / 4); /* BL - entry code */ 697 698 lputl(0xef000011); /* SWI - exit code */ 699 lputl(segtext.filelen+HEADR); /* text size */ 700 lputl(segdata.filelen); /* data size */ 701 lputl(0); /* sym size */ 702 703 lputl(segdata.len - segdata.filelen); /* bss size */ 704 lputl(0); /* sym type */ 705 lputl(INITTEXT-HEADR); /* text addr */ 706 lputl(0); /* workspace - ignored */ 707 708 lputl(32); /* addr mode / data addr flag */ 709 lputl(0); /* data addr */ 710 for(t=0; t<2; t++) 711 lputl(0); /* reserved */ 712 713 for(t=0; t<15; t++) 714 lputl(0xe1a00000); /* NOP - zero init code */ 715 lputl(0xe1a0f00e); /* B (R14) - zero init return */ 716 break; 717 case Hplan9x32: /* plan 9 */ 718 lput(0x647); /* magic */ 719 lput(segtext.filelen); /* sizes */ 720 lput(segdata.filelen); 721 lput(segdata.len - segdata.filelen); 722 lput(symsize); /* nsyms */ 723 lput(entryvalue()); /* va of entry */ 724 lput(0L); 725 lput(lcsize); 726 break; 727 case Hixp1200: /* boot for IXP1200 */ 728 break; 729 case Hipaq: /* boot for ipaq */ 730 lputl(0xe3300000); /* nop */ 731 lputl(0xe3300000); /* nop */ 732 lputl(0xe3300000); /* nop */ 733 lputl(0xe3300000); /* nop */ 734 break; 735 case Hlinux: 736 case Hfreebsd: 737 case Hnetbsd: 738 case Hopenbsd: 739 asmbelf(symo); 740 break; 741 } 742 cflush(); 743 if(debug['c']){ 744 print("textsize=%ulld\n", segtext.filelen); 745 print("datsize=%ulld\n", segdata.filelen); 746 print("bsssize=%ulld\n", segdata.len - segdata.filelen); 747 print("symsize=%d\n", symsize); 748 print("lcsize=%d\n", lcsize); 749 print("total=%lld\n", segtext.filelen+segdata.len+symsize+lcsize); 750 } 751 } 752 753 /* 754 void 755 cput(int32 c) 756 { 757 *cbp++ = c; 758 if(--cbc <= 0) 759 cflush(); 760 } 761 */ 762 763 void 764 wput(int32 l) 765 { 766 767 cbp[0] = l>>8; 768 cbp[1] = l; 769 cbp += 2; 770 cbc -= 2; 771 if(cbc <= 0) 772 cflush(); 773 } 774 775 776 void 777 hput(int32 l) 778 { 779 780 cbp[0] = l>>8; 781 cbp[1] = l; 782 cbp += 2; 783 cbc -= 2; 784 if(cbc <= 0) 785 cflush(); 786 } 787 788 void 789 lput(int32 l) 790 { 791 792 cbp[0] = l>>24; 793 cbp[1] = l>>16; 794 cbp[2] = l>>8; 795 cbp[3] = l; 796 cbp += 4; 797 cbc -= 4; 798 if(cbc <= 0) 799 cflush(); 800 } 801 802 void 803 nopstat(char *f, Count *c) 804 { 805 if(c->outof) 806 Bprint(&bso, "%s delay %d/%d (%.2f)\n", f, 807 c->outof - c->count, c->outof, 808 (double)(c->outof - c->count)/c->outof); 809 } 810 811 void 812 asmout(Prog *p, Optab *o, int32 *out, Sym *gmsym) 813 { 814 int32 o1, o2, o3, o4, o5, o6, v; 815 int r, rf, rt, rt2; 816 Reloc *rel; 817 818 PP = p; 819 o1 = 0; 820 o2 = 0; 821 o3 = 0; 822 o4 = 0; 823 o5 = 0; 824 o6 = 0; 825 armsize += o->size; 826 if(debug['P']) print("%ux: %P type %d\n", (uint32)(p->pc), p, o->type); 827 switch(o->type) { 828 default: 829 diag("unknown asm %d", o->type); 830 prasm(p); 831 break; 832 833 case 0: /* pseudo ops */ 834 if(debug['G']) print("%ux: %s: arm %d\n", (uint32)(p->pc), p->from.sym->name, p->from.sym->fnptr); 835 break; 836 837 case 1: /* op R,[R],R */ 838 o1 = oprrr(p->as, p->scond); 839 rf = p->from.reg; 840 rt = p->to.reg; 841 r = p->reg; 842 if(p->to.type == D_NONE) 843 rt = 0; 844 if(p->as == AMOVB || p->as == AMOVH || p->as == AMOVW || p->as == AMVN) 845 r = 0; 846 else 847 if(r == NREG) 848 r = rt; 849 o1 |= rf | (r<<16) | (rt<<12); 850 break; 851 852 case 2: /* movbu $I,[R],R */ 853 aclass(&p->from); 854 o1 = oprrr(p->as, p->scond); 855 o1 |= immrot(instoffset); 856 rt = p->to.reg; 857 r = p->reg; 858 if(p->to.type == D_NONE) 859 rt = 0; 860 if(p->as == AMOVW || p->as == AMVN) 861 r = 0; 862 else if(r == NREG) 863 r = rt; 864 o1 |= (r<<16) | (rt<<12); 865 break; 866 867 case 3: /* add R<<[IR],[R],R */ 868 mov: 869 aclass(&p->from); 870 o1 = oprrr(p->as, p->scond); 871 o1 |= p->from.offset; 872 rt = p->to.reg; 873 r = p->reg; 874 if(p->to.type == D_NONE) 875 rt = 0; 876 if(p->as == AMOVW || p->as == AMVN) 877 r = 0; 878 else if(r == NREG) 879 r = rt; 880 o1 |= (r<<16) | (rt<<12); 881 break; 882 883 case 4: /* add $I,[R],R */ 884 aclass(&p->from); 885 o1 = oprrr(AADD, p->scond); 886 o1 |= immrot(instoffset); 887 r = p->from.reg; 888 if(r == NREG) 889 r = o->param; 890 o1 |= r << 16; 891 o1 |= p->to.reg << 12; 892 break; 893 894 case 5: /* bra s */ 895 o1 = opbra(p->as, p->scond); 896 v = -8; 897 if(p->to.sym != S && p->to.sym->type != 0) { 898 rel = addrel(cursym); 899 rel->off = pc - cursym->value; 900 rel->siz = 4; 901 rel->sym = p->to.sym; 902 rel->add = o1 | ((v >> 2) & 0xffffff); 903 rel->type = D_CALL; 904 break; 905 } 906 if(p->cond != P) 907 v = (p->cond->pc - pc) - 8; 908 o1 |= (v >> 2) & 0xffffff; 909 break; 910 911 case 6: /* b ,O(R) -> add $O,R,PC */ 912 aclass(&p->to); 913 o1 = oprrr(AADD, p->scond); 914 o1 |= immrot(instoffset); 915 o1 |= p->to.reg << 16; 916 o1 |= REGPC << 12; 917 break; 918 919 case 7: /* bl (R) -> blx R */ 920 aclass(&p->to); 921 if(instoffset != 0) 922 diag("%P: doesn't support BL offset(REG) where offset != 0", p); 923 o1 = oprrr(ABL, p->scond); 924 o1 |= p->to.reg; 925 break; 926 927 case 8: /* sll $c,[R],R -> mov (R<<$c),R */ 928 aclass(&p->from); 929 o1 = oprrr(p->as, p->scond); 930 r = p->reg; 931 if(r == NREG) 932 r = p->to.reg; 933 o1 |= r; 934 o1 |= (instoffset&31) << 7; 935 o1 |= p->to.reg << 12; 936 break; 937 938 case 9: /* sll R,[R],R -> mov (R<<R),R */ 939 o1 = oprrr(p->as, p->scond); 940 r = p->reg; 941 if(r == NREG) 942 r = p->to.reg; 943 o1 |= r; 944 o1 |= (p->from.reg << 8) | (1<<4); 945 o1 |= p->to.reg << 12; 946 break; 947 948 case 10: /* swi [$con] */ 949 o1 = oprrr(p->as, p->scond); 950 if(p->to.type != D_NONE) { 951 aclass(&p->to); 952 o1 |= instoffset & 0xffffff; 953 } 954 break; 955 956 case 11: /* word */ 957 aclass(&p->to); 958 o1 = instoffset; 959 if(p->to.sym != S) { 960 rel = addrel(cursym); 961 rel->off = pc - cursym->value; 962 rel->siz = 4; 963 rel->sym = p->to.sym; 964 rel->add = p->to.offset; 965 if(rel->sym == gmsym) { 966 rel->type = D_TLS; 967 if(flag_shared) 968 rel->add += pc - p->pcrel->pc - 8 - rel->siz; 969 rel->xadd = rel->add; 970 rel->xsym = rel->sym; 971 } else if(flag_shared) { 972 rel->type = D_PCREL; 973 rel->add += pc - p->pcrel->pc - 8; 974 } else 975 rel->type = D_ADDR; 976 o1 = 0; 977 } 978 break; 979 980 case 12: /* movw $lcon, reg */ 981 o1 = omvl(p, &p->from, p->to.reg); 982 if(o->flag & LPCREL) { 983 o2 = oprrr(AADD, p->scond) | p->to.reg | REGPC << 16 | p->to.reg << 12; 984 } 985 break; 986 987 case 13: /* op $lcon, [R], R */ 988 o1 = omvl(p, &p->from, REGTMP); 989 if(!o1) 990 break; 991 o2 = oprrr(p->as, p->scond); 992 o2 |= REGTMP; 993 r = p->reg; 994 if(p->as == AMOVW || p->as == AMVN) 995 r = 0; 996 else if(r == NREG) 997 r = p->to.reg; 998 o2 |= r << 16; 999 if(p->to.type != D_NONE) 1000 o2 |= p->to.reg << 12; 1001 break; 1002 1003 case 14: /* movb/movbu/movh/movhu R,R */ 1004 o1 = oprrr(ASLL, p->scond); 1005 1006 if(p->as == AMOVBU || p->as == AMOVHU) 1007 o2 = oprrr(ASRL, p->scond); 1008 else 1009 o2 = oprrr(ASRA, p->scond); 1010 1011 r = p->to.reg; 1012 o1 |= (p->from.reg)|(r<<12); 1013 o2 |= (r)|(r<<12); 1014 if(p->as == AMOVB || p->as == AMOVBS || p->as == AMOVBU) { 1015 o1 |= (24<<7); 1016 o2 |= (24<<7); 1017 } else { 1018 o1 |= (16<<7); 1019 o2 |= (16<<7); 1020 } 1021 break; 1022 1023 case 15: /* mul r,[r,]r */ 1024 o1 = oprrr(p->as, p->scond); 1025 rf = p->from.reg; 1026 rt = p->to.reg; 1027 r = p->reg; 1028 if(r == NREG) 1029 r = rt; 1030 if(rt == r) { 1031 r = rf; 1032 rf = rt; 1033 } 1034 if(0) 1035 if(rt == r || rf == REGPC || r == REGPC || rt == REGPC) { 1036 diag("bad registers in MUL"); 1037 prasm(p); 1038 } 1039 o1 |= (rf<<8) | r | (rt<<16); 1040 break; 1041 1042 1043 case 16: /* div r,[r,]r */ 1044 o1 = 0xf << 28; 1045 o2 = 0; 1046 break; 1047 1048 case 17: 1049 o1 = oprrr(p->as, p->scond); 1050 rf = p->from.reg; 1051 rt = p->to.reg; 1052 rt2 = p->to.offset; 1053 r = p->reg; 1054 o1 |= (rf<<8) | r | (rt<<16) | (rt2<<12); 1055 break; 1056 1057 case 20: /* mov/movb/movbu R,O(R) */ 1058 aclass(&p->to); 1059 r = p->to.reg; 1060 if(r == NREG) 1061 r = o->param; 1062 o1 = osr(p->as, p->from.reg, instoffset, r, p->scond); 1063 break; 1064 1065 case 21: /* mov/movbu O(R),R -> lr */ 1066 aclass(&p->from); 1067 r = p->from.reg; 1068 if(r == NREG) 1069 r = o->param; 1070 o1 = olr(instoffset, r, p->to.reg, p->scond); 1071 if(p->as != AMOVW) 1072 o1 |= 1<<22; 1073 break; 1074 1075 case 30: /* mov/movb/movbu R,L(R) */ 1076 o1 = omvl(p, &p->to, REGTMP); 1077 if(!o1) 1078 break; 1079 r = p->to.reg; 1080 if(r == NREG) 1081 r = o->param; 1082 o2 = osrr(p->from.reg, REGTMP,r, p->scond); 1083 if(p->as != AMOVW) 1084 o2 |= 1<<22; 1085 break; 1086 1087 case 31: /* mov/movbu L(R),R -> lr[b] */ 1088 o1 = omvl(p, &p->from, REGTMP); 1089 if(!o1) 1090 break; 1091 r = p->from.reg; 1092 if(r == NREG) 1093 r = o->param; 1094 o2 = olrr(REGTMP,r, p->to.reg, p->scond); 1095 if(p->as == AMOVBU || p->as == AMOVBS || p->as == AMOVB) 1096 o2 |= 1<<22; 1097 break; 1098 1099 case 34: /* mov $lacon,R */ 1100 o1 = omvl(p, &p->from, REGTMP); 1101 if(!o1) 1102 break; 1103 1104 o2 = oprrr(AADD, p->scond); 1105 o2 |= REGTMP; 1106 r = p->from.reg; 1107 if(r == NREG) 1108 r = o->param; 1109 o2 |= r << 16; 1110 if(p->to.type != D_NONE) 1111 o2 |= p->to.reg << 12; 1112 break; 1113 1114 case 35: /* mov PSR,R */ 1115 o1 = (2<<23) | (0xf<<16) | (0<<0); 1116 o1 |= (p->scond & C_SCOND) << 28; 1117 o1 |= (p->from.reg & 1) << 22; 1118 o1 |= p->to.reg << 12; 1119 break; 1120 1121 case 36: /* mov R,PSR */ 1122 o1 = (2<<23) | (0x29f<<12) | (0<<4); 1123 if(p->scond & C_FBIT) 1124 o1 ^= 0x010 << 12; 1125 o1 |= (p->scond & C_SCOND) << 28; 1126 o1 |= (p->to.reg & 1) << 22; 1127 o1 |= p->from.reg << 0; 1128 break; 1129 1130 case 37: /* mov $con,PSR */ 1131 aclass(&p->from); 1132 o1 = (2<<23) | (0x29f<<12) | (0<<4); 1133 if(p->scond & C_FBIT) 1134 o1 ^= 0x010 << 12; 1135 o1 |= (p->scond & C_SCOND) << 28; 1136 o1 |= immrot(instoffset); 1137 o1 |= (p->to.reg & 1) << 22; 1138 o1 |= p->from.reg << 0; 1139 break; 1140 1141 case 38: /* movm $con,oreg -> stm */ 1142 o1 = (0x4 << 25); 1143 o1 |= p->from.offset & 0xffff; 1144 o1 |= p->to.reg << 16; 1145 aclass(&p->to); 1146 goto movm; 1147 1148 case 39: /* movm oreg,$con -> ldm */ 1149 o1 = (0x4 << 25) | (1 << 20); 1150 o1 |= p->to.offset & 0xffff; 1151 o1 |= p->from.reg << 16; 1152 aclass(&p->from); 1153 movm: 1154 if(instoffset != 0) 1155 diag("offset must be zero in MOVM"); 1156 o1 |= (p->scond & C_SCOND) << 28; 1157 if(p->scond & C_PBIT) 1158 o1 |= 1 << 24; 1159 if(p->scond & C_UBIT) 1160 o1 |= 1 << 23; 1161 if(p->scond & C_SBIT) 1162 o1 |= 1 << 22; 1163 if(p->scond & C_WBIT) 1164 o1 |= 1 << 21; 1165 break; 1166 1167 case 40: /* swp oreg,reg,reg */ 1168 aclass(&p->from); 1169 if(instoffset != 0) 1170 diag("offset must be zero in SWP"); 1171 o1 = (0x2<<23) | (0x9<<4); 1172 if(p->as != ASWPW) 1173 o1 |= 1 << 22; 1174 o1 |= p->from.reg << 16; 1175 o1 |= p->reg << 0; 1176 o1 |= p->to.reg << 12; 1177 o1 |= (p->scond & C_SCOND) << 28; 1178 break; 1179 1180 case 41: /* rfe -> movm.s.w.u 0(r13),[r15] */ 1181 o1 = 0xe8fd8000; 1182 break; 1183 1184 case 50: /* floating point store */ 1185 v = regoff(&p->to); 1186 r = p->to.reg; 1187 if(r == NREG) 1188 r = o->param; 1189 o1 = ofsr(p->as, p->from.reg, v, r, p->scond, p); 1190 break; 1191 1192 case 51: /* floating point load */ 1193 v = regoff(&p->from); 1194 r = p->from.reg; 1195 if(r == NREG) 1196 r = o->param; 1197 o1 = ofsr(p->as, p->to.reg, v, r, p->scond, p) | (1<<20); 1198 break; 1199 1200 case 52: /* floating point store, int32 offset UGLY */ 1201 o1 = omvl(p, &p->to, REGTMP); 1202 if(!o1) 1203 break; 1204 r = p->to.reg; 1205 if(r == NREG) 1206 r = o->param; 1207 o2 = oprrr(AADD, p->scond) | (REGTMP << 12) | (REGTMP << 16) | r; 1208 o3 = ofsr(p->as, p->from.reg, 0, REGTMP, p->scond, p); 1209 break; 1210 1211 case 53: /* floating point load, int32 offset UGLY */ 1212 o1 = omvl(p, &p->from, REGTMP); 1213 if(!o1) 1214 break; 1215 r = p->from.reg; 1216 if(r == NREG) 1217 r = o->param; 1218 o2 = oprrr(AADD, p->scond) | (REGTMP << 12) | (REGTMP << 16) | r; 1219 o3 = ofsr(p->as, p->to.reg, 0, REGTMP, p->scond, p) | (1<<20); 1220 break; 1221 1222 case 54: /* floating point arith */ 1223 o1 = oprrr(p->as, p->scond); 1224 rf = p->from.reg; 1225 rt = p->to.reg; 1226 r = p->reg; 1227 if(r == NREG) { 1228 r = rt; 1229 if(p->as == AMOVF || p->as == AMOVD || p->as == ASQRTF || p->as == ASQRTD || p->as == AABSF || p->as == AABSD) 1230 r = 0; 1231 } 1232 o1 |= rf | (r<<16) | (rt<<12); 1233 break; 1234 1235 case 56: /* move to FP[CS]R */ 1236 o1 = ((p->scond & C_SCOND) << 28) | (0xe << 24) | (1<<8) | (1<<4); 1237 o1 |= ((p->to.reg+1)<<21) | (p->from.reg << 12); 1238 break; 1239 1240 case 57: /* move from FP[CS]R */ 1241 o1 = ((p->scond & C_SCOND) << 28) | (0xe << 24) | (1<<8) | (1<<4); 1242 o1 |= ((p->from.reg+1)<<21) | (p->to.reg<<12) | (1<<20); 1243 break; 1244 case 58: /* movbu R,R */ 1245 o1 = oprrr(AAND, p->scond); 1246 o1 |= immrot(0xff); 1247 rt = p->to.reg; 1248 r = p->from.reg; 1249 if(p->to.type == D_NONE) 1250 rt = 0; 1251 if(r == NREG) 1252 r = rt; 1253 o1 |= (r<<16) | (rt<<12); 1254 break; 1255 1256 case 59: /* movw/bu R<<I(R),R -> ldr indexed */ 1257 if(p->from.reg == NREG) { 1258 if(p->as != AMOVW) 1259 diag("byte MOV from shifter operand"); 1260 goto mov; 1261 } 1262 if(p->from.offset&(1<<4)) 1263 diag("bad shift in LDR"); 1264 o1 = olrr(p->from.offset, p->from.reg, p->to.reg, p->scond); 1265 if(p->as == AMOVBU) 1266 o1 |= 1<<22; 1267 break; 1268 1269 case 60: /* movb R(R),R -> ldrsb indexed */ 1270 if(p->from.reg == NREG) { 1271 diag("byte MOV from shifter operand"); 1272 goto mov; 1273 } 1274 if(p->from.offset&(~0xf)) 1275 diag("bad shift in LDRSB"); 1276 o1 = olhrr(p->from.offset, p->from.reg, p->to.reg, p->scond); 1277 o1 ^= (1<<5)|(1<<6); 1278 break; 1279 1280 case 61: /* movw/b/bu R,R<<[IR](R) -> str indexed */ 1281 if(p->to.reg == NREG) 1282 diag("MOV to shifter operand"); 1283 o1 = osrr(p->from.reg, p->to.offset, p->to.reg, p->scond); 1284 if(p->as == AMOVB || p->as == AMOVBS || p->as == AMOVBU) 1285 o1 |= 1<<22; 1286 break; 1287 1288 case 62: /* case R -> movw R<<2(PC),PC */ 1289 if(o->flag & LPCREL) { 1290 o1 = oprrr(AADD, p->scond) | immrot(1) | p->from.reg << 16 | REGTMP << 12; 1291 o2 = olrr(REGTMP, REGPC, REGTMP, p->scond); 1292 o2 |= 2<<7; 1293 o3 = oprrr(AADD, p->scond) | REGTMP | REGPC << 16 | REGPC << 12; 1294 } else { 1295 o1 = olrr(p->from.reg, REGPC, REGPC, p->scond); 1296 o1 |= 2<<7; 1297 } 1298 break; 1299 1300 case 63: /* bcase */ 1301 if(p->cond != P) { 1302 rel = addrel(cursym); 1303 rel->off = pc - cursym->value; 1304 rel->siz = 4; 1305 if(p->to.sym != S && p->to.sym->type != 0) { 1306 rel->sym = p->to.sym; 1307 rel->add = p->to.offset; 1308 } else { 1309 rel->sym = cursym; 1310 rel->add = p->cond->pc - cursym->value; 1311 } 1312 if(o->flag & LPCREL) { 1313 rel->type = D_PCREL; 1314 rel->add += pc - p->pcrel->pc - 16 + rel->siz; 1315 } else 1316 rel->type = D_ADDR; 1317 o1 = 0; 1318 } 1319 break; 1320 1321 /* reloc ops */ 1322 case 64: /* mov/movb/movbu R,addr */ 1323 o1 = omvl(p, &p->to, REGTMP); 1324 if(!o1) 1325 break; 1326 o2 = osr(p->as, p->from.reg, 0, REGTMP, p->scond); 1327 if(o->flag & LPCREL) { 1328 o3 = o2; 1329 o2 = oprrr(AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; 1330 } 1331 break; 1332 1333 case 65: /* mov/movbu addr,R */ 1334 o1 = omvl(p, &p->from, REGTMP); 1335 if(!o1) 1336 break; 1337 o2 = olr(0, REGTMP, p->to.reg, p->scond); 1338 if(p->as == AMOVBU || p->as == AMOVBS || p->as == AMOVB) 1339 o2 |= 1<<22; 1340 if(o->flag & LPCREL) { 1341 o3 = o2; 1342 o2 = oprrr(AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; 1343 } 1344 break; 1345 1346 case 68: /* floating point store -> ADDR */ 1347 o1 = omvl(p, &p->to, REGTMP); 1348 if(!o1) 1349 break; 1350 o2 = ofsr(p->as, p->from.reg, 0, REGTMP, p->scond, p); 1351 if(o->flag & LPCREL) { 1352 o3 = o2; 1353 o2 = oprrr(AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; 1354 } 1355 break; 1356 1357 case 69: /* floating point load <- ADDR */ 1358 o1 = omvl(p, &p->from, REGTMP); 1359 if(!o1) 1360 break; 1361 o2 = ofsr(p->as, p->to.reg, 0, REGTMP, p->scond, p) | (1<<20); 1362 if(o->flag & LPCREL) { 1363 o3 = o2; 1364 o2 = oprrr(AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; 1365 } 1366 break; 1367 1368 /* ArmV4 ops: */ 1369 case 70: /* movh/movhu R,O(R) -> strh */ 1370 aclass(&p->to); 1371 r = p->to.reg; 1372 if(r == NREG) 1373 r = o->param; 1374 o1 = oshr(p->from.reg, instoffset, r, p->scond); 1375 break; 1376 case 71: /* movb/movh/movhu O(R),R -> ldrsb/ldrsh/ldrh */ 1377 aclass(&p->from); 1378 r = p->from.reg; 1379 if(r == NREG) 1380 r = o->param; 1381 o1 = olhr(instoffset, r, p->to.reg, p->scond); 1382 if(p->as == AMOVB || p->as == AMOVBS) 1383 o1 ^= (1<<5)|(1<<6); 1384 else if(p->as == AMOVH || p->as == AMOVHS) 1385 o1 ^= (1<<6); 1386 break; 1387 case 72: /* movh/movhu R,L(R) -> strh */ 1388 o1 = omvl(p, &p->to, REGTMP); 1389 if(!o1) 1390 break; 1391 r = p->to.reg; 1392 if(r == NREG) 1393 r = o->param; 1394 o2 = oshrr(p->from.reg, REGTMP,r, p->scond); 1395 break; 1396 case 73: /* movb/movh/movhu L(R),R -> ldrsb/ldrsh/ldrh */ 1397 o1 = omvl(p, &p->from, REGTMP); 1398 if(!o1) 1399 break; 1400 r = p->from.reg; 1401 if(r == NREG) 1402 r = o->param; 1403 o2 = olhrr(REGTMP, r, p->to.reg, p->scond); 1404 if(p->as == AMOVB || p->as == AMOVBS) 1405 o2 ^= (1<<5)|(1<<6); 1406 else if(p->as == AMOVH || p->as == AMOVHS) 1407 o2 ^= (1<<6); 1408 break; 1409 case 74: /* bx $I */ 1410 diag("ABX $I"); 1411 break; 1412 case 75: /* bx O(R) */ 1413 aclass(&p->to); 1414 if(instoffset != 0) 1415 diag("non-zero offset in ABX"); 1416 /* 1417 o1 = oprrr(AADD, p->scond) | immrot(0) | (REGPC<<16) | (REGLINK<<12); // mov PC, LR 1418 o2 = ((p->scond&C_SCOND)<<28) | (0x12fff<<8) | (1<<4) | p->to.reg; // BX R 1419 */ 1420 // p->to.reg may be REGLINK 1421 o1 = oprrr(AADD, p->scond); 1422 o1 |= immrot(instoffset); 1423 o1 |= p->to.reg << 16; 1424 o1 |= REGTMP << 12; 1425 o2 = oprrr(AADD, p->scond) | immrot(0) | (REGPC<<16) | (REGLINK<<12); // mov PC, LR 1426 o3 = ((p->scond&C_SCOND)<<28) | (0x12fff<<8) | (1<<4) | REGTMP; // BX Rtmp 1427 break; 1428 case 76: /* bx O(R) when returning from fn*/ 1429 diag("ABXRET"); 1430 break; 1431 case 77: /* ldrex oreg,reg */ 1432 aclass(&p->from); 1433 if(instoffset != 0) 1434 diag("offset must be zero in LDREX"); 1435 o1 = (0x19<<20) | (0xf9f); 1436 o1 |= p->from.reg << 16; 1437 o1 |= p->to.reg << 12; 1438 o1 |= (p->scond & C_SCOND) << 28; 1439 break; 1440 case 78: /* strex reg,oreg,reg */ 1441 aclass(&p->from); 1442 if(instoffset != 0) 1443 diag("offset must be zero in STREX"); 1444 o1 = (0x18<<20) | (0xf90); 1445 o1 |= p->from.reg << 16; 1446 o1 |= p->reg << 0; 1447 o1 |= p->to.reg << 12; 1448 o1 |= (p->scond & C_SCOND) << 28; 1449 break; 1450 case 80: /* fmov zfcon,freg */ 1451 if(p->as == AMOVD) { 1452 o1 = 0xeeb00b00; // VMOV imm 64 1453 o2 = oprrr(ASUBD, p->scond); 1454 } else { 1455 o1 = 0x0eb00a00; // VMOV imm 32 1456 o2 = oprrr(ASUBF, p->scond); 1457 } 1458 v = 0x70; // 1.0 1459 r = p->to.reg; 1460 1461 // movf $1.0, r 1462 o1 |= (p->scond & C_SCOND) << 28; 1463 o1 |= r << 12; 1464 o1 |= (v&0xf) << 0; 1465 o1 |= (v&0xf0) << 12; 1466 1467 // subf r,r,r 1468 o2 |= r | (r<<16) | (r<<12); 1469 break; 1470 case 81: /* fmov sfcon,freg */ 1471 o1 = 0x0eb00a00; // VMOV imm 32 1472 if(p->as == AMOVD) 1473 o1 = 0xeeb00b00; // VMOV imm 64 1474 o1 |= (p->scond & C_SCOND) << 28; 1475 o1 |= p->to.reg << 12; 1476 v = chipfloat(&p->from.ieee); 1477 o1 |= (v&0xf) << 0; 1478 o1 |= (v&0xf0) << 12; 1479 break; 1480 case 82: /* fcmp freg,freg, */ 1481 o1 = oprrr(p->as, p->scond); 1482 o1 |= (p->reg<<12) | (p->from.reg<<0); 1483 o2 = 0x0ef1fa10; // VMRS R15 1484 o2 |= (p->scond & C_SCOND) << 28; 1485 break; 1486 case 83: /* fcmp freg,, */ 1487 o1 = oprrr(p->as, p->scond); 1488 o1 |= (p->from.reg<<12) | (1<<16); 1489 o2 = 0x0ef1fa10; // VMRS R15 1490 o2 |= (p->scond & C_SCOND) << 28; 1491 break; 1492 case 84: /* movfw freg,freg - truncate float-to-fix */ 1493 o1 = oprrr(p->as, p->scond); 1494 o1 |= (p->from.reg<<0); 1495 o1 |= (p->to.reg<<12); 1496 break; 1497 case 85: /* movwf freg,freg - fix-to-float */ 1498 o1 = oprrr(p->as, p->scond); 1499 o1 |= (p->from.reg<<0); 1500 o1 |= (p->to.reg<<12); 1501 break; 1502 case 86: /* movfw freg,reg - truncate float-to-fix */ 1503 // macro for movfw freg,FTMP; movw FTMP,reg 1504 o1 = oprrr(p->as, p->scond); 1505 o1 |= (p->from.reg<<0); 1506 o1 |= (FREGTMP<<12); 1507 o2 = oprrr(AMOVFW+AEND, p->scond); 1508 o2 |= (FREGTMP<<16); 1509 o2 |= (p->to.reg<<12); 1510 break; 1511 case 87: /* movwf reg,freg - fix-to-float */ 1512 // macro for movw reg,FTMP; movwf FTMP,freg 1513 o1 = oprrr(AMOVWF+AEND, p->scond); 1514 o1 |= (p->from.reg<<12); 1515 o1 |= (FREGTMP<<16); 1516 o2 = oprrr(p->as, p->scond); 1517 o2 |= (FREGTMP<<0); 1518 o2 |= (p->to.reg<<12); 1519 break; 1520 case 88: /* movw reg,freg */ 1521 o1 = oprrr(AMOVWF+AEND, p->scond); 1522 o1 |= (p->from.reg<<12); 1523 o1 |= (p->to.reg<<16); 1524 break; 1525 case 89: /* movw freg,reg */ 1526 o1 = oprrr(AMOVFW+AEND, p->scond); 1527 o1 |= (p->from.reg<<16); 1528 o1 |= (p->to.reg<<12); 1529 break; 1530 case 90: /* tst reg */ 1531 o1 = oprrr(ACMP+AEND, p->scond); 1532 o1 |= p->from.reg<<16; 1533 break; 1534 case 91: /* ldrexd oreg,reg */ 1535 aclass(&p->from); 1536 if(instoffset != 0) 1537 diag("offset must be zero in LDREX"); 1538 o1 = (0x1b<<20) | (0xf9f); 1539 o1 |= p->from.reg << 16; 1540 o1 |= p->to.reg << 12; 1541 o1 |= (p->scond & C_SCOND) << 28; 1542 break; 1543 case 92: /* strexd reg,oreg,reg */ 1544 aclass(&p->from); 1545 if(instoffset != 0) 1546 diag("offset must be zero in STREX"); 1547 o1 = (0x1a<<20) | (0xf90); 1548 o1 |= p->from.reg << 16; 1549 o1 |= p->reg << 0; 1550 o1 |= p->to.reg << 12; 1551 o1 |= (p->scond & C_SCOND) << 28; 1552 break; 1553 case 93: /* movb/movh/movhu addr,R -> ldrsb/ldrsh/ldrh */ 1554 o1 = omvl(p, &p->from, REGTMP); 1555 if(!o1) 1556 break; 1557 o2 = olhr(0, REGTMP, p->to.reg, p->scond); 1558 if(p->as == AMOVB || p->as == AMOVBS) 1559 o2 ^= (1<<5)|(1<<6); 1560 else if(p->as == AMOVH || p->as == AMOVHS) 1561 o2 ^= (1<<6); 1562 if(o->flag & LPCREL) { 1563 o3 = o2; 1564 o2 = oprrr(AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; 1565 } 1566 break; 1567 case 94: /* movh/movhu R,addr -> strh */ 1568 o1 = omvl(p, &p->to, REGTMP); 1569 if(!o1) 1570 break; 1571 o2 = oshr(p->from.reg, 0, REGTMP, p->scond); 1572 if(o->flag & LPCREL) { 1573 o3 = o2; 1574 o2 = oprrr(AADD, p->scond) | REGTMP | REGPC << 16 | REGTMP << 12; 1575 } 1576 break; 1577 case 95: /* PLD off(reg) */ 1578 o1 = 0xf5d0f000; 1579 o1 |= p->from.reg << 16; 1580 if(p->from.offset < 0) { 1581 o1 &= ~(1 << 23); 1582 o1 |= (-p->from.offset) & 0xfff; 1583 } else 1584 o1 |= p->from.offset & 0xfff; 1585 break; 1586 case 96: /* UNDEF */ 1587 // This is supposed to be something that stops execution. 1588 // It's not supposed to be reached, ever, but if it is, we'd 1589 // like to be able to tell how we got there. Assemble as 1590 // 0xf7fabcfd which is guranteed to raise undefined instruction 1591 // exception. 1592 o1 = 0xf7fabcfd; 1593 break; 1594 case 97: /* CLZ Rm, Rd */ 1595 o1 = oprrr(p->as, p->scond); 1596 o1 |= p->to.reg << 12; 1597 o1 |= p->from.reg; 1598 break; 1599 case 98: /* MULW{T,B} Rs, Rm, Rd */ 1600 o1 = oprrr(p->as, p->scond); 1601 o1 |= p->to.reg << 16; 1602 o1 |= p->from.reg << 8; 1603 o1 |= p->reg; 1604 break; 1605 case 99: /* MULAW{T,B} Rs, Rm, Rn, Rd */ 1606 o1 = oprrr(p->as, p->scond); 1607 o1 |= p->to.reg << 12; 1608 o1 |= p->from.reg << 8; 1609 o1 |= p->reg; 1610 o1 |= p->to.offset << 16; 1611 break; 1612 } 1613 1614 out[0] = o1; 1615 out[1] = o2; 1616 out[2] = o3; 1617 out[3] = o4; 1618 out[4] = o5; 1619 out[5] = o6; 1620 return; 1621 1622 #ifdef NOTDEF 1623 v = p->pc; 1624 switch(o->size) { 1625 default: 1626 if(debug['a']) 1627 Bprint(&bso, " %.8ux:\t\t%P\n", v, p); 1628 break; 1629 case 4: 1630 if(debug['a']) 1631 Bprint(&bso, " %.8ux: %.8ux\t%P\n", v, o1, p); 1632 lputl(o1); 1633 break; 1634 case 8: 1635 if(debug['a']) 1636 Bprint(&bso, " %.8ux: %.8ux %.8ux%P\n", v, o1, o2, p); 1637 lputl(o1); 1638 lputl(o2); 1639 break; 1640 case 12: 1641 if(debug['a']) 1642 Bprint(&bso, " %.8ux: %.8ux %.8ux %.8ux%P\n", v, o1, o2, o3, p); 1643 lputl(o1); 1644 lputl(o2); 1645 lputl(o3); 1646 break; 1647 case 16: 1648 if(debug['a']) 1649 Bprint(&bso, " %.8ux: %.8ux %.8ux %.8ux %.8ux%P\n", 1650 v, o1, o2, o3, o4, p); 1651 lputl(o1); 1652 lputl(o2); 1653 lputl(o3); 1654 lputl(o4); 1655 break; 1656 case 20: 1657 if(debug['a']) 1658 Bprint(&bso, " %.8ux: %.8ux %.8ux %.8ux %.8ux %.8ux%P\n", 1659 v, o1, o2, o3, o4, o5, p); 1660 lputl(o1); 1661 lputl(o2); 1662 lputl(o3); 1663 lputl(o4); 1664 lputl(o5); 1665 break; 1666 case 24: 1667 if(debug['a']) 1668 Bprint(&bso, " %.8ux: %.8ux %.8ux %.8ux %.8ux %.8ux %.8ux%P\n", 1669 v, o1, o2, o3, o4, o5, o6, p); 1670 lputl(o1); 1671 lputl(o2); 1672 lputl(o3); 1673 lputl(o4); 1674 lputl(o5); 1675 lputl(o6); 1676 break; 1677 } 1678 #endif 1679 } 1680 1681 int32 1682 oprrr(int a, int sc) 1683 { 1684 int32 o; 1685 1686 o = (sc & C_SCOND) << 28; 1687 if(sc & C_SBIT) 1688 o |= 1 << 20; 1689 if(sc & (C_PBIT|C_WBIT)) 1690 diag(".P/.W on dp instruction"); 1691 switch(a) { 1692 case AMULU: 1693 case AMUL: return o | (0x0<<21) | (0x9<<4); 1694 case AMULA: return o | (0x1<<21) | (0x9<<4); 1695 case AMULLU: return o | (0x4<<21) | (0x9<<4); 1696 case AMULL: return o | (0x6<<21) | (0x9<<4); 1697 case AMULALU: return o | (0x5<<21) | (0x9<<4); 1698 case AMULAL: return o | (0x7<<21) | (0x9<<4); 1699 case AAND: return o | (0x0<<21); 1700 case AEOR: return o | (0x1<<21); 1701 case ASUB: return o | (0x2<<21); 1702 case ARSB: return o | (0x3<<21); 1703 case AADD: return o | (0x4<<21); 1704 case AADC: return o | (0x5<<21); 1705 case ASBC: return o | (0x6<<21); 1706 case ARSC: return o | (0x7<<21); 1707 case ATST: return o | (0x8<<21) | (1<<20); 1708 case ATEQ: return o | (0x9<<21) | (1<<20); 1709 case ACMP: return o | (0xa<<21) | (1<<20); 1710 case ACMN: return o | (0xb<<21) | (1<<20); 1711 case AORR: return o | (0xc<<21); 1712 case AMOVB: 1713 case AMOVH: 1714 case AMOVW: return o | (0xd<<21); 1715 case ABIC: return o | (0xe<<21); 1716 case AMVN: return o | (0xf<<21); 1717 case ASLL: return o | (0xd<<21) | (0<<5); 1718 case ASRL: return o | (0xd<<21) | (1<<5); 1719 case ASRA: return o | (0xd<<21) | (2<<5); 1720 case ASWI: return o | (0xf<<24); 1721 1722 case AADDD: return o | (0xe<<24) | (0x3<<20) | (0xb<<8) | (0<<4); 1723 case AADDF: return o | (0xe<<24) | (0x3<<20) | (0xa<<8) | (0<<4); 1724 case ASUBD: return o | (0xe<<24) | (0x3<<20) | (0xb<<8) | (4<<4); 1725 case ASUBF: return o | (0xe<<24) | (0x3<<20) | (0xa<<8) | (4<<4); 1726 case AMULD: return o | (0xe<<24) | (0x2<<20) | (0xb<<8) | (0<<4); 1727 case AMULF: return o | (0xe<<24) | (0x2<<20) | (0xa<<8) | (0<<4); 1728 case ADIVD: return o | (0xe<<24) | (0x8<<20) | (0xb<<8) | (0<<4); 1729 case ADIVF: return o | (0xe<<24) | (0x8<<20) | (0xa<<8) | (0<<4); 1730 case ASQRTD: return o | (0xe<<24) | (0xb<<20) | (1<<16) | (0xb<<8) | (0xc<<4); 1731 case ASQRTF: return o | (0xe<<24) | (0xb<<20) | (1<<16) | (0xa<<8) | (0xc<<4); 1732 case AABSD: return o | (0xe<<24) | (0xb<<20) | (0<<16) | (0xb<<8) | (0xc<<4); 1733 case AABSF: return o | (0xe<<24) | (0xb<<20) | (0<<16) | (0xa<<8) | (0xc<<4); 1734 case ACMPD: return o | (0xe<<24) | (0xb<<20) | (4<<16) | (0xb<<8) | (0xc<<4); 1735 case ACMPF: return o | (0xe<<24) | (0xb<<20) | (4<<16) | (0xa<<8) | (0xc<<4); 1736 1737 case AMOVF: return o | (0xe<<24) | (0xb<<20) | (0<<16) | (0xa<<8) | (4<<4); 1738 case AMOVD: return o | (0xe<<24) | (0xb<<20) | (0<<16) | (0xb<<8) | (4<<4); 1739 1740 case AMOVDF: return o | (0xe<<24) | (0xb<<20) | (7<<16) | (0xa<<8) | (0xc<<4) | 1741 (1<<8); // dtof 1742 case AMOVFD: return o | (0xe<<24) | (0xb<<20) | (7<<16) | (0xa<<8) | (0xc<<4) | 1743 (0<<8); // dtof 1744 1745 case AMOVWF: 1746 if((sc & C_UBIT) == 0) 1747 o |= 1<<7; /* signed */ 1748 return o | (0xe<<24) | (0xb<<20) | (8<<16) | (0xa<<8) | (4<<4) | 1749 (0<<18) | (0<<8); // toint, double 1750 case AMOVWD: 1751 if((sc & C_UBIT) == 0) 1752 o |= 1<<7; /* signed */ 1753 return o | (0xe<<24) | (0xb<<20) | (8<<16) | (0xa<<8) | (4<<4) | 1754 (0<<18) | (1<<8); // toint, double 1755 1756 case AMOVFW: 1757 if((sc & C_UBIT) == 0) 1758 o |= 1<<16; /* signed */ 1759 return o | (0xe<<24) | (0xb<<20) | (8<<16) | (0xa<<8) | (4<<4) | 1760 (1<<18) | (0<<8) | (1<<7); // toint, double, trunc 1761 case AMOVDW: 1762 if((sc & C_UBIT) == 0) 1763 o |= 1<<16; /* signed */ 1764 return o | (0xe<<24) | (0xb<<20) | (8<<16) | (0xa<<8) | (4<<4) | 1765 (1<<18) | (1<<8) | (1<<7); // toint, double, trunc 1766 1767 case AMOVWF+AEND: // copy WtoF 1768 return o | (0xe<<24) | (0x0<<20) | (0xb<<8) | (1<<4); 1769 case AMOVFW+AEND: // copy FtoW 1770 return o | (0xe<<24) | (0x1<<20) | (0xb<<8) | (1<<4); 1771 case ACMP+AEND: // cmp imm 1772 return o | (0x3<<24) | (0x5<<20); 1773 1774 case ACLZ: 1775 // CLZ doesn't support .S 1776 return (o & (0xf<<28)) | (0x16f<<16) | (0xf1<<4); 1777 1778 case AMULWT: 1779 return (o & (0xf<<28)) | (0x12 << 20) | (0xe<<4); 1780 case AMULWB: 1781 return (o & (0xf<<28)) | (0x12 << 20) | (0xa<<4); 1782 case AMULAWT: 1783 return (o & (0xf<<28)) | (0x12 << 20) | (0xc<<4); 1784 case AMULAWB: 1785 return (o & (0xf<<28)) | (0x12 << 20) | (0x8<<4); 1786 1787 case ABL: // BLX REG 1788 return (o & (0xf<<28)) | (0x12fff3 << 4); 1789 } 1790 diag("bad rrr %d", a); 1791 prasm(curp); 1792 return 0; 1793 } 1794 1795 int32 1796 opbra(int a, int sc) 1797 { 1798 1799 if(sc & (C_SBIT|C_PBIT|C_WBIT)) 1800 diag(".S/.P/.W on bra instruction"); 1801 sc &= C_SCOND; 1802 if(a == ABL) 1803 return (sc<<28)|(0x5<<25)|(0x1<<24); 1804 if(sc != 0xe) 1805 diag(".COND on bcond instruction"); 1806 switch(a) { 1807 case ABEQ: return (0x0<<28)|(0x5<<25); 1808 case ABNE: return (0x1<<28)|(0x5<<25); 1809 case ABCS: return (0x2<<28)|(0x5<<25); 1810 case ABHS: return (0x2<<28)|(0x5<<25); 1811 case ABCC: return (0x3<<28)|(0x5<<25); 1812 case ABLO: return (0x3<<28)|(0x5<<25); 1813 case ABMI: return (0x4<<28)|(0x5<<25); 1814 case ABPL: return (0x5<<28)|(0x5<<25); 1815 case ABVS: return (0x6<<28)|(0x5<<25); 1816 case ABVC: return (0x7<<28)|(0x5<<25); 1817 case ABHI: return (0x8<<28)|(0x5<<25); 1818 case ABLS: return (0x9<<28)|(0x5<<25); 1819 case ABGE: return (0xa<<28)|(0x5<<25); 1820 case ABLT: return (0xb<<28)|(0x5<<25); 1821 case ABGT: return (0xc<<28)|(0x5<<25); 1822 case ABLE: return (0xd<<28)|(0x5<<25); 1823 case AB: return (0xe<<28)|(0x5<<25); 1824 } 1825 diag("bad bra %A", a); 1826 prasm(curp); 1827 return 0; 1828 } 1829 1830 int32 1831 olr(int32 v, int b, int r, int sc) 1832 { 1833 int32 o; 1834 1835 if(sc & C_SBIT) 1836 diag(".S on LDR/STR instruction"); 1837 o = (sc & C_SCOND) << 28; 1838 if(!(sc & C_PBIT)) 1839 o |= 1 << 24; 1840 if(!(sc & C_UBIT)) 1841 o |= 1 << 23; 1842 if(sc & C_WBIT) 1843 o |= 1 << 21; 1844 o |= (1<<26) | (1<<20); 1845 if(v < 0) { 1846 if(sc & C_UBIT) diag(".U on neg offset"); 1847 v = -v; 1848 o ^= 1 << 23; 1849 } 1850 if(v >= (1<<12) || v < 0) 1851 diag("literal span too large: %d (R%d)\n%P", v, b, PP); 1852 o |= v; 1853 o |= b << 16; 1854 o |= r << 12; 1855 return o; 1856 } 1857 1858 int32 1859 olhr(int32 v, int b, int r, int sc) 1860 { 1861 int32 o; 1862 1863 if(sc & C_SBIT) 1864 diag(".S on LDRH/STRH instruction"); 1865 o = (sc & C_SCOND) << 28; 1866 if(!(sc & C_PBIT)) 1867 o |= 1 << 24; 1868 if(sc & C_WBIT) 1869 o |= 1 << 21; 1870 o |= (1<<23) | (1<<20)|(0xb<<4); 1871 if(v < 0) { 1872 v = -v; 1873 o ^= 1 << 23; 1874 } 1875 if(v >= (1<<8) || v < 0) 1876 diag("literal span too large: %d (R%d)\n%P", v, b, PP); 1877 o |= (v&0xf)|((v>>4)<<8)|(1<<22); 1878 o |= b << 16; 1879 o |= r << 12; 1880 return o; 1881 } 1882 1883 int32 1884 osr(int a, int r, int32 v, int b, int sc) 1885 { 1886 int32 o; 1887 1888 o = olr(v, b, r, sc) ^ (1<<20); 1889 if(a != AMOVW) 1890 o |= 1<<22; 1891 return o; 1892 } 1893 1894 int32 1895 oshr(int r, int32 v, int b, int sc) 1896 { 1897 int32 o; 1898 1899 o = olhr(v, b, r, sc) ^ (1<<20); 1900 return o; 1901 } 1902 1903 1904 int32 1905 osrr(int r, int i, int b, int sc) 1906 { 1907 1908 return olr(i, b, r, sc) ^ ((1<<25) | (1<<20)); 1909 } 1910 1911 int32 1912 oshrr(int r, int i, int b, int sc) 1913 { 1914 return olhr(i, b, r, sc) ^ ((1<<22) | (1<<20)); 1915 } 1916 1917 int32 1918 olrr(int i, int b, int r, int sc) 1919 { 1920 1921 return olr(i, b, r, sc) ^ (1<<25); 1922 } 1923 1924 int32 1925 olhrr(int i, int b, int r, int sc) 1926 { 1927 return olhr(i, b, r, sc) ^ (1<<22); 1928 } 1929 1930 int32 1931 ofsr(int a, int r, int32 v, int b, int sc, Prog *p) 1932 { 1933 int32 o; 1934 1935 if(sc & C_SBIT) 1936 diag(".S on FLDR/FSTR instruction"); 1937 o = (sc & C_SCOND) << 28; 1938 if(!(sc & C_PBIT)) 1939 o |= 1 << 24; 1940 if(sc & C_WBIT) 1941 o |= 1 << 21; 1942 o |= (6<<25) | (1<<24) | (1<<23) | (10<<8); 1943 if(v < 0) { 1944 v = -v; 1945 o ^= 1 << 23; 1946 } 1947 if(v & 3) 1948 diag("odd offset for floating point op: %d\n%P", v, p); 1949 else 1950 if(v >= (1<<10) || v < 0) 1951 diag("literal span too large: %d\n%P", v, p); 1952 o |= (v>>2) & 0xFF; 1953 o |= b << 16; 1954 o |= r << 12; 1955 1956 switch(a) { 1957 default: 1958 diag("bad fst %A", a); 1959 case AMOVD: 1960 o |= 1 << 8; 1961 case AMOVF: 1962 break; 1963 } 1964 return o; 1965 } 1966 1967 int32 1968 omvl(Prog *p, Adr *a, int dr) 1969 { 1970 int32 v, o1; 1971 if(!p->cond) { 1972 aclass(a); 1973 v = immrot(~instoffset); 1974 if(v == 0) { 1975 diag("missing literal"); 1976 prasm(p); 1977 return 0; 1978 } 1979 o1 = oprrr(AMVN, p->scond&C_SCOND); 1980 o1 |= v; 1981 o1 |= dr << 12; 1982 } else { 1983 v = p->cond->pc - p->pc - 8; 1984 o1 = olr(v, REGPC, dr, p->scond&C_SCOND); 1985 } 1986 return o1; 1987 } 1988 1989 int 1990 chipzero(Ieee *e) 1991 { 1992 // We use GOARM=7 to gate the use of VFPv3 vmov (imm) instructions. 1993 if(goarm < 7 || e->l != 0 || e->h != 0) 1994 return -1; 1995 return 0; 1996 } 1997 1998 int 1999 chipfloat(Ieee *e) 2000 { 2001 int n; 2002 ulong h; 2003 2004 // We use GOARM=7 to gate the use of VFPv3 vmov (imm) instructions. 2005 if(goarm < 7) 2006 goto no; 2007 2008 if(e->l != 0 || (e->h&0xffff) != 0) 2009 goto no; 2010 h = e->h & 0x7fc00000; 2011 if(h != 0x40000000 && h != 0x3fc00000) 2012 goto no; 2013 n = 0; 2014 2015 // sign bit (a) 2016 if(e->h & 0x80000000) 2017 n |= 1<<7; 2018 2019 // exp sign bit (b) 2020 if(h == 0x3fc00000) 2021 n |= 1<<6; 2022 2023 // rest of exp and mantissa (cd-efgh) 2024 n |= (e->h >> 16) & 0x3f; 2025 2026 //print("match %.8lux %.8lux %d\n", e->l, e->h, n); 2027 return n; 2028 2029 no: 2030 return -1; 2031 }