| @@ -433,32 +433,36 @@ static void gcall_or_jmp(int docall) · 432 unmodified lines |
| 433 | } | 433 | } |
| 434 | } | 434 | } |
| 435 | | 435 | |
| 436 | static int pass_in_freg(CType *type, int regs) | 436 | static void reg_pass(CType *type, int *rc, int *fieldofs, int ofs) |
| 437 | { | 437 | { |
| 438 | int tr; | | |
| 439 | int toplevel = !regs; | | |
| 440 | if ((type->t & VT_BTYPE) == VT_STRUCT) { | 438 | if ((type->t & VT_BTYPE) == VT_STRUCT) { |
| 441 | Sym *f; | 439 | Sym *f; |
| 442 | if (type->ref->type.t == VT_UNION) | 440 | if (type->ref->type.t == VT_UNION) |
| 443 | return toplevel ? 0 : -1; | 441 | rc[0] = -1; |
| 444 | for (f = type->ref->next; f; f = f->next) { | 442 | else for (f = type->ref->next; f; f = f->next) |
| 445 | tr = pass_in_freg(&f->type, regs); | 443 | reg_pass(&f->type, rc, fieldofs, ofs + f->c); |
| 446 | if (tr < 0 || (regs + tr) > 2) | | |
| 447 | return toplevel ? 0 : -1; | | |
| 448 | regs += tr; | | |
| 449 | } | | |
| 450 | return regs; | | |
| 451 | } else if (type->t & VT_ARRAY) { | 444 | } else if (type->t & VT_ARRAY) { |
| 452 | if (type->ref->c < 0 || type->ref->c > 2) | 445 | if (type->ref->c < 0 || type->ref->c > 2) |
| 453 | return toplevel ? 0 : -1; | 446 | rc[0] = -1; |
| 454 | tr = pass_in_freg(&type->ref->type, regs); | 447 | else { |
| 455 | tr *= type->ref->c; | 448 | int a, sz = type_size(&type->ref->type, &a); |
| 456 | if (tr < 0 || (regs + tr) > 2) | 449 | reg_pass(&type->ref->type, rc, fieldofs, ofs); |
| 457 | return toplevel ? 0 : -1; | 450 | if (rc[0] > 2 || (rc[0] == 2 && type->ref->c > 1)) |
| 458 | return regs + tr; | 451 | rc[0] = -1; |
| 459 | } | 452 | else if (type->ref->c == 2 && rc[0] && rc[1] == RC_FLOAT) { |
| 460 | return is_float(type->t) && (type->t & VT_BTYPE) != VT_LDOUBLE | 453 | rc[++rc[0]] = RC_FLOAT; |
| 461 | ? 1 : toplevel ? 0 : -1; | 454 | fieldofs[rc[0]] = ((ofs + sz) << 4) |
| | 455 | | (type->ref->type.t & VT_BTYPE); |
| | 456 | } else if (type->ref->c == 2) |
| | 457 | rc[0] = -1; |
| | 458 | } |
| | 459 | } else if (rc[0] == 2 || rc[0] < 0 || (type->t & VT_BTYPE) == VT_LDOUBLE) |
| | 460 | rc[0] = -1; |
| | 461 | else if (!rc[0] || rc[1] == RC_FLOAT || is_float(type->t)) { |
| | 462 | rc[++rc[0]] = is_float(type->t) ? RC_FLOAT : RC_INT; |
| | 463 | fieldofs[rc[0]] = (ofs << 4) | (type->t & VT_BTYPE); |
| | 464 | } else |
| | 465 | rc[0] = -1; |
| 462 | } | 466 | } |
| 463 | | 467 | |
| 464 | ST_FUNC void gfunc_call(int nb_args) | 468 | ST_FUNC void gfunc_call(int nb_args) |
| @@ -472,7 +476,9 @@ ST_FUNC void gfunc_call(int nb_args) · 7 unmodified lines |
| 472 | aireg = afreg = 0; | 476 | aireg = afreg = 0; |
| 473 | sa = vtop[-nb_args].type.ref->next; | 477 | sa = vtop[-nb_args].type.ref->next; |
| 474 | for (i = 0; i < nb_args; i++) { | 478 | for (i = 0; i < nb_args; i++) { |
| 475 | int *pareg, nregs, infreg = 0, byref = 0, tempofs; | 479 | int *pareg, nregs, byref = 0, tempofs; |
| | 480 | int prc[3], fieldofs[3]; |
| | 481 | prc[0] = 0; |
| 476 | sv = &vtop[1 + i - nb_args]; | 482 | sv = &vtop[1 + i - nb_args]; |
| 477 | sv->type.t &= ~VT_ARRAY; // XXX this should be done in tccgen.c | 483 | sv->type.t &= ~VT_ARRAY; // XXX this should be done in tccgen.c |
| 478 | size = type_size(&sv->type, &align); | 484 | size = type_size(&sv->type, &align); |
| @@ -490,31 +496,43 @@ ST_FUNC void gfunc_call(int nb_args) · 11 unmodified lines |
| 490 | else | 496 | else |
| 491 | nregs = 1; | 497 | nregs = 1; |
| 492 | if (!sa) { | 498 | if (!sa) { |
| 493 | infreg = 0; | 499 | prc[0] = nregs, prc[1] = prc[2] = RC_INT; |
| 494 | } else { | 500 | } else { |
| 495 | infreg = pass_in_freg(&sv->type, 0); | 501 | reg_pass(&sv->type, prc, fieldofs, 0); |
| | 502 | if (prc[0] < 0) |
| | 503 | prc[0] = nregs, prc[1] = prc[2] = RC_INT; |
| 496 | } | 504 | } |
| 497 | if (!infreg && !sa && align == 2*XLEN && size <= 2*XLEN) | 505 | if (!sa && align == 2*XLEN && size <= 2*XLEN) |
| 498 | aireg = (aireg + 1) & ~1; | 506 | aireg = (aireg + 1) & ~1; |
| 499 | pareg = infreg ? &afreg : &aireg; | 507 | nregs = prc[0]; |
| 500 | if ((*pareg < 8 && (!infreg || (*pareg + nregs) <= 8)) && !force_stack) { | 508 | assert(nregs); |
| 501 | info[i] = *pareg + (infreg ? 8 : 0); | 509 | if (!nregs |
| 502 | (*pareg)++; | 510 | || (prc[1] == RC_INT && aireg >= 8) |
| 503 | if (nregs == 1) | 511 | || (prc[1] == RC_FLOAT && afreg >= 8) |
| 504 | ; | 512 | || (nregs == 2 && prc[2] == RC_FLOAT && afreg >= 7) |
| 505 | else if (*pareg < 8) | 513 | || (nregs == 2 && prc[1] != prc[2] && (afreg >= 8 || aireg >= 8)) |
| 506 | (*pareg)++; | 514 | || force_stack) { |
| 507 | else { | | |
| 508 | info[i] |= 16; | | |
| 509 | stack_adj += 8; | | |
| 510 | } | | |
| 511 | } else { | | |
| 512 | info[i] = 32; | 515 | info[i] = 32; |
| 513 | if (align < XLEN) | 516 | if (align < XLEN) |
| 514 | align = XLEN; | 517 | align = XLEN; |
| 515 | stack_adj += (size + align - 1) & -align; | 518 | stack_adj += (size + align - 1) & -align; |
| 516 | if (!sa) | 519 | if (!sa) |
| 517 | force_stack = 1; | 520 | force_stack = 1; |
| | 521 | } else { |
| | 522 | if (prc[1] == RC_FLOAT) |
| | 523 | info[i] = 8 + afreg++; |
| | 524 | else |
| | 525 | info[i] = aireg++; |
| | 526 | if (nregs == 2) { |
| | 527 | if (prc[2] == RC_FLOAT) |
| | 528 | info[i] |= (1 + 8 + afreg++) << 7; |
| | 529 | else if (aireg < 8) |
| | 530 | info[i] |= (1 + aireg++) << 7; |
| | 531 | else { |
| | 532 | info[i] |= 16; |
| | 533 | stack_adj += 8; |
| | 534 | } |
| | 535 | } |
| 518 | } | 536 | } |
| 519 | if (byref) | 537 | if (byref) |
| 520 | info[i] |= 64 | (tempofs << 7); | 538 | info[i] |= 64 | (tempofs << 7); |
| @@ -569,62 +587,72 @@ ST_FUNC void gfunc_call(int nb_args) · 48 unmodified lines |
| 569 | } | 587 | } |
| 570 | } | 588 | } |
| 571 | for (i = 0; i < nb_args; i++) { | 589 | for (i = 0; i < nb_args; i++) { |
| 572 | int r = info[nb_args - 1 - i]; | 590 | int r = info[nb_args - 1 - i], r2 = r; |
| 573 | if (!(r & 32)) { | 591 | if (!(r & 32)) { |
| 574 | CType origtype; | 592 | CType origtype; |
| | 593 | int prc[3], fieldofs[3], loadt; |
| | 594 | prc[0] = 0; |
| 575 | r &= 15; | 595 | r &= 15; |
| | 596 | r2 = r2 & 64 ? 0 : r2 >> 7; |
| 576 | vrotb(i+1); | 597 | vrotb(i+1); |
| 577 | origtype = vtop->type; | 598 | origtype = vtop->type; |
| 578 | size = type_size(&vtop->type, &align); | 599 | size = type_size(&vtop->type, &align); |
| 579 | if (r >= 8 && (vtop->r & VT_LVAL)) { | 600 | reg_pass(&vtop->type, prc, fieldofs, 0); |
| 580 | int infreg = pass_in_freg(&vtop->type, 0); | 601 | if (prc[0] <= 0 || (!r2 && prc[0] > 1)) { |
| 581 | int loadt = vtop->type.t & VT_BTYPE; | 602 | prc[0] = (size + 7) >> 3; |
| | 603 | prc[1] = prc[2] = RC_INT; |
| | 604 | fieldofs[1] = (0 << 4) | (size <= 1 ? VT_BYTE : size <= 2 ? VT_SHORT : size <= 4 ? VT_INT : VT_LLONG); |
| | 605 | fieldofs[2] = (8 << 4) | (size <= 9 ? VT_BYTE : size <= 10 ? VT_SHORT : size <= 12 ? VT_INT : VT_LLONG); |
| | 606 | } |
| | 607 | loadt = vtop->type.t & VT_BTYPE; |
| | 608 | if (loadt == VT_STRUCT) { |
| | 609 | loadt = fieldofs[1] & VT_BTYPE; |
| | 610 | } |
| | 611 | if (info[nb_args - 1 - i] & 16) { |
| | 612 | assert(!r2); |
| | 613 | r2 = 1 + TREG_RA; |
| | 614 | } |
| | 615 | if (loadt == VT_LDOUBLE) { |
| | 616 | assert(r2); |
| | 617 | r2--; |
| | 618 | } else if (r2) { |
| | 619 | test_lvalue(); |
| | 620 | vpushv(vtop); |
| | 621 | } |
| | 622 | vtop->type.t = loadt; |
| | 623 | gv(r < 8 ? RC_R(r) : RC_F(r - 8)); |
| | 624 | vtop->type = origtype; |
| | 625 | |
| | 626 | if (r2 && loadt != VT_LDOUBLE) { |
| | 627 | r2--; |
| | 628 | assert(r2 < 16 || r2 == TREG_RA); |
| | 629 | vswap(); |
| | 630 | gaddrof(); |
| | 631 | vtop->type = char_pointer_type; |
| | 632 | vpushi(fieldofs[2] >> 4); |
| | 633 | gen_op('+'); |
| | 634 | indir(); |
| | 635 | vtop->type = origtype; |
| | 636 | loadt = vtop->type.t & VT_BTYPE; |
| 582 | if (loadt == VT_STRUCT) { | 637 | if (loadt == VT_STRUCT) { |
| 583 | if (infreg == 1) | 638 | loadt = fieldofs[2] & VT_BTYPE; |
| 584 | loadt = size == 4 ? VT_FLOAT : VT_DOUBLE; | | |
| 585 | else | | |
| 586 | loadt = size == 8 ? VT_FLOAT : VT_DOUBLE; | | |
| 587 | } | 639 | } |
| | 640 | save_reg_upstack(r2, 1); |
| 588 | vtop->type.t = loadt; | 641 | vtop->type.t = loadt; |
| 589 | assert (infreg && infreg <= 2); | 642 | load(r2, vtop); |
| 590 | save_reg_upstack(r, 1); | 643 | assert(r2 < VT_CONST); |
| 591 | if (infreg > 1) { | | |
| 592 | save_reg_upstack(r + 1, 1); | | |
| 593 | test_lvalue(); | | |
| 594 | } | | |
| 595 | load(r, vtop); | | |
| 596 | vset(&origtype, r, 0); | | |
| 597 | vswap(); | | |
| 598 | if (infreg > 1) { | | |
| 599 | assert(r + 1 < 16); | | |
| 600 | gaddrof(); | | |
| 601 | mk_pointer(&vtop->type); | | |
| 602 | vpushi(1); | | |
| 603 | gen_op('+'); | | |
| 604 | indir(); | | |
| 605 | load(r + 1, vtop); | | |
| 606 | vtop[-1].r2 = r + 1; | | |
| 607 | } | | |
| 608 | vtop--; | 644 | vtop--; |
| 609 | } else { | 645 | vtop->r2 = r2; |
| 610 | if (r < 8 && size > 8 && (vtop->type.t & VT_BTYPE) == VT_STRUCT) | | |
| 611 | vtop->type.t = VT_LDOUBLE; // force loading a pair of regs | | |
| 612 | gv(r < 8 ? RC_R(r) : RC_F(r - 8)); | | |
| 613 | vtop->type = origtype; | | |
| 614 | if (size > 8) { | | |
| 615 | assert((vtop->type.t & VT_BTYPE) == VT_LDOUBLE | | |
| 616 | || (vtop->type.t & VT_BTYPE) == VT_STRUCT); | | |
| 617 | assert(vtop->r2 < VT_CONST); | | |
| 618 | if (info[nb_args - 1 - i] & 16) { | | |
| 619 | ES(0x23, 3, 2, ireg(vtop->r2), splitofs); // sd t0, ofs(sp) | | |
| 620 | } else if (vtop->r2 != 1 + vtop->r) { | | |
| 621 | assert(vtop->r < 7); | | |
| 622 | /* XXX we'd like to have 'gv' move directly into | | |
| 623 | the right class instead of us fixing it up. */ | | |
| 624 | EI(0x13, 0, ireg(vtop->r) + 1, ireg(vtop->r2), 0); // mv Ra+1, RR2 | | |
| 625 | vtop->r2 = 1 + vtop->r; | | |
| 626 | } | | |
| 627 | } | 646 | } |
| | 647 | if (info[nb_args - 1 - i] & 16) { |
| | 648 | ES(0x23, 3, 2, ireg(vtop->r2), splitofs); // sd t0, ofs(sp) |
| | 649 | vtop->r2 = VT_CONST; |
| | 650 | } else if (loadt == VT_LDOUBLE && vtop->r2 != r2) { |
| | 651 | assert(vtop->r2 <= 7 && r2 <= 7); |
| | 652 | /* XXX we'd like to have 'gv' move directly into |
| | 653 | the right class instead of us fixing it up. */ |
| | 654 | EI(0x13, 0, ireg(r2), ireg(vtop->r2), 0); // mv Ra+1, RR2 |
| | 655 | vtop->r2 = r2; |
| 628 | } | 656 | } |
| 629 | vrott(i+1); | 657 | vrott(i+1); |
| 630 | } | 658 | } |
| @@ -681,19 +709,29 @@ ST_FUNC void gfunc_prolog(CType *func_type) · 50 unmodified lines |
| 681 | param_addr = addr; | 709 | param_addr = addr; |
| 682 | addr += size; | 710 | addr += size; |
| 683 | } else { | 711 | } else { |
| 684 | int regcount = 1, *pareg = &aireg; | 712 | int regcount, *pareg; |
| 685 | if ((regcount = pass_in_freg(type, 0))) | 713 | int prc[3], fieldofs[3]; |
| 686 | pareg = &afreg; | 714 | prc[0] = 0; |
| 687 | else { | 715 | reg_pass(type, prc, fieldofs, 0); |
| 688 | regcount = 1; | 716 | if (prc[0] <= 0) { |
| | 717 | prc[0] = (size + 7) >> 3; |
| | 718 | prc[1] = prc[2] = RC_INT; |
| | 719 | fieldofs[1] = (0 << 4) | (size <= 1 ? VT_BYTE : size <= 2 ? VT_SHORT : size <= 4 ? VT_INT : VT_LLONG); |
| | 720 | fieldofs[2] = (8 << 4) | (size <= 9 ? VT_BYTE : size <= 10 ? VT_SHORT : size <= 12 ? VT_INT : VT_LLONG); |
| 689 | } | 721 | } |
| 690 | if (regcount + *pareg > 8) | 722 | regcount = prc[0]; |
| | 723 | if (!regcount |
| | 724 | || (prc[1] == RC_INT && aireg >= 8) |
| | 725 | || (prc[1] == RC_FLOAT && afreg >= 8) |
| | 726 | || (regcount == 2 && prc[2] == RC_FLOAT && afreg >= 7) |
| | 727 | || (regcount == 2 && prc[1] != prc[2] && (afreg >= 8 || aireg >= 8))) |
| 691 | goto from_stack; | 728 | goto from_stack; |
| 692 | if (size > XLEN && pareg == &aireg) | 729 | if (size > XLEN) |
| 693 | regcount++; | 730 | assert(regcount == 2); |
| 694 | loc -= regcount * 8; // XXX could reserve only 'size' bytes | 731 | loc -= regcount * 8; // XXX could reserve only 'size' bytes |
| 695 | param_addr = loc; | 732 | param_addr = loc; |
| 696 | for (i = 0; i < regcount; i++) { | 733 | for (i = 0; i < regcount; i++) { |
| | 734 | pareg = prc[1+i] == RC_INT ? &aireg : &afreg; |
| 697 | if (*pareg >= 8) { | 735 | if (*pareg >= 8) { |
| 698 | assert(i == 1 && regcount == 2 && !(addr & 7)); | 736 | assert(i == 1 && regcount == 2 && !(addr & 7)); |
| 699 | EI(0x03, 3, 5, 8, addr); // ld t0, addr(s0) | 737 | EI(0x03, 3, 5, 8, addr); // ld t0, addr(s0) |
| @@ -703,11 +741,10 @@ ST_FUNC void gfunc_prolog(CType *func_type) · 3 unmodified lines |
| 703 | } | 741 | } |
| 704 | if (pareg == &afreg) { | 742 | if (pareg == &afreg) { |
| 705 | //assert(type->t == VT_FLOAT || type->t == VT_DOUBLE); | 743 | //assert(type->t == VT_FLOAT || type->t == VT_DOUBLE); |
| 706 | ES(0x27, (size / regcount) == 4 ? 2 : 3, 8, 10 + *pareg, loc + i*(size / regcount)); // fs[wd] FAi, loc(s0) | 744 | ES(0x27, (size / regcount) == 4 ? 2 : 3, 8, 10 + afreg++, loc + (fieldofs[i+1] >> 4)); // fs[wd] FAi, loc(s0) |
| 707 | } else { | 745 | } else { |
| 708 | ES(0x23, 3, 8, 10 + *pareg, loc + i*8); // sd aX, loc(s0) // XXX | 746 | ES(0x23, 3, 8, 10 + aireg++, loc + i*8); // sd aX, loc(s0) // XXX |
| 709 | } | 747 | } |
| 710 | (*pareg)++; | | |
| 711 | } | 748 | } |
| 712 | } | 749 | } |
| 713 | sym_push(sym->v & ~SYM_FIELD, &sym->type, | 750 | sym_push(sym->v & ~SYM_FIELD, &sym->type, |
| @@ -727,57 +764,50 @@ ST_FUNC void gfunc_prolog(CType *func_type) · 13 unmodified lines |
| 727 | ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, | 764 | ST_FUNC int gfunc_sret(CType *vt, int variadic, CType *ret, |
| 728 | int *ret_align, int *regsize) | 765 | int *ret_align, int *regsize) |
| 729 | { | 766 | { |
| 730 | /* generic code can only deal with structs of pow(2) sizes | 767 | int align, size = type_size(vt, &align), infreg, nregs; |
| 731 | (it always deals with whole registers), so go through our own | 768 | int prc[3], fieldofs[3]; |
| 732 | code. */ | | |
| 733 | int align, size = type_size(vt, &align), infreg; | | |
| 734 | *ret_align = 1; | 769 | *ret_align = 1; |
| 735 | *regsize = 8; | 770 | *regsize = 8; |
| 736 | if (size > 16) | 771 | if (size > 16) |
| 737 | return 0; | 772 | return 0; |
| 738 | infreg = pass_in_freg(vt, 0); | 773 | prc[0] = 0; |
| 739 | if (infreg) { | 774 | reg_pass(vt, prc, fieldofs, 0); |
| 740 | *regsize = size / infreg; | 775 | if (prc[0] <= 0) { |
| 741 | ret->t = (size / infreg) == 4 ? VT_FLOAT : VT_DOUBLE; | 776 | prc[0] = (size + 7) >> 3; |
| 742 | return infreg; | 777 | prc[1] = prc[2] = RC_INT; |
| | 778 | fieldofs[1] = (0 << 4) | (size <= 1 ? VT_BYTE : size <= 2 ? VT_SHORT : size <= 4 ? VT_INT : VT_LLONG); |
| | 779 | fieldofs[2] = (8 << 4) | (size <= 9 ? VT_BYTE : size <= 10 ? VT_SHORT : size <= 12 ? VT_INT : VT_LLONG); |
| 743 | } | 780 | } |
| 744 | if (size > 8) | 781 | nregs = prc[0]; |
| 745 | ret->t = VT_LLONG; | 782 | assert(nregs > 0); |
| 746 | else if (size > 4) | 783 | if (nregs == 2 && prc[1] != prc[2]) |
| 747 | ret->t = VT_LLONG; | 784 | return -1; /* generic code can't deal with this case */ |
| 748 | else if (size > 2) | 785 | if (prc[1] == RC_FLOAT) { |
| 749 | ret->t = VT_INT; | 786 | *regsize = size / nregs; |
| 750 | else if (size > 1) | 787 | } |
| 751 | ret->t = VT_SHORT; | 788 | ret->t = fieldofs[1] & VT_BTYPE; |
| 752 | else | 789 | return nregs; |
| 753 | ret->t = VT_BYTE; | | |
| 754 | return (size + 7) / 8; | | |
| 755 | } | 790 | } |
| 756 | | 791 | |
| 757 | ST_FUNC void gfunc_return2(CType *func_type) | 792 | ST_FUNC void arch_transfer_ret_regs(int aftercall) |
| 758 | { | 793 | { |
| 759 | int align, size = type_size(func_type, &align), nregs; | 794 | int prc[3], fieldofs[3]; |
| 760 | CType type = *func_type; | 795 | prc[0] = 0; |
| 761 | if (size > 2 * XLEN) { | 796 | reg_pass(&vtop->type, prc, fieldofs, 0); |
| 762 | mk_pointer(&type); | 797 | assert(prc[0] == 2 && prc[1] != prc[2] && !(fieldofs[1] >> 4)); |
| 763 | vset(&type, VT_LOCAL | VT_LVAL, func_vc); | 798 | assert(vtop->r == (VT_LOCAL | VT_LVAL)); |
| 764 | indir(); | 799 | vpushv(vtop); |
| 765 | vswap(); | 800 | vtop->type.t = fieldofs[1] & VT_BTYPE; |
| 766 | vstore(); | 801 | if (aftercall) |
| 767 | vpop(); | 802 | store(prc[1] == RC_INT ? REG_IRET : REG_FRET, vtop); |
| 768 | return; | 803 | else |
| 769 | } | 804 | load(prc[1] == RC_INT ? REG_IRET : REG_FRET, vtop); |
| 770 | nregs = pass_in_freg(func_type, 0); | 805 | vtop->c.i += fieldofs[2] >> 4; |
| 771 | if (!nregs) { | 806 | vtop->type.t = fieldofs[2] & VT_BTYPE; |
| 772 | nregs = (size + 7) / 8; | 807 | if (aftercall) |
| 773 | if (nregs == 2) | 808 | store(prc[2] == RC_INT ? REG_IRET : REG_FRET, vtop); |
| 774 | vtop->type.t = VT_LDOUBLE; | | |
| 775 | } | | |
| 776 | | | |
| 777 | if (is_float(func_type->t) && (vtop->type.t & VT_BTYPE) != VT_LDOUBLE) | | |
| 778 | gv(RC_FRET); | | |
| 779 | else | 809 | else |
| 780 | gv(RC_IRET); | 810 | load(prc[2] == RC_INT ? REG_IRET : REG_FRET, vtop); |
| 781 | vtop--; | 811 | vtop--; |
| 782 | } | 812 | } |
| 783 | | 813 | |