P1pp.P1pp (21409B)
1 # p1pp.P1pp -- libp1pp v1, portable utility library for P1pp programs. 2 # 3 # Concatenated after the P1 backend header and frontend, and before user 4 # source: 5 # 6 # catm P1-<arch>.M1pp P1.M1pp p1pp.P1pp usersrc.P1pp > program.M1 7 # 8 # Targets P1-64 only (WORD = 8). All internal labels use the 9 # `libp1pp__` prefix; public entry points are unprefixed. 10 # 11 # See docs/LIBP1PP.md for the public contract. 12 13 # ========================================================================= 14 # Compile-time helpers 15 # ========================================================================= 16 17 # %alignup -- Round `rs` up to a multiple of `align` (a power of two) and 18 # place the result in `rd`. `scratch` is clobbered. `align` must be a 19 # constant integer expression. Two instructions plus an %li for the mask: 20 # 21 # rd = rs + (align - 1) 22 # scratch = -align (i.e. ~(align-1) for power-of-two align) 23 # rd = rd & scratch 24 %macro alignup(rd, rs, align, scratch) 25 %addi(rd, rs, (- align 1)) 26 %li(scratch, (- 0 align)) 27 %and(rd, rd, scratch) 28 %endm 29 30 # ========================================================================= 31 # Control-flow macros 32 # ========================================================================= 33 # 34 # Every conditional block macro uses a uniform three-branch lowering that 35 # works for all seven P1 conditions (including LT, LTU, LTZ which have no 36 # inverted branch): load a "take the body" target, branch on cc, then 37 # unconditionally skip past the body. 38 39 # ---- %if_<cc> ----------------------------------------------------------- 40 41 %macro if_eq(ra, rb, body) 42 %beq(ra, rb, &@body) 43 %b(&@end) 44 :@body 45 body 46 :@end 47 %endm 48 49 %macro if_ne(ra, rb, body) 50 %bne(ra, rb, &@body) 51 %b(&@end) 52 :@body 53 body 54 :@end 55 %endm 56 57 %macro if_lt(ra, rb, body) 58 %blt(ra, rb, &@body) 59 %b(&@end) 60 :@body 61 body 62 :@end 63 %endm 64 65 %macro if_ltu(ra, rb, body) 66 %bltu(ra, rb, &@body) 67 %b(&@end) 68 :@body 69 body 70 :@end 71 %endm 72 73 %macro if_eqz(ra, body) 74 %beqz(ra, &@body) 75 %b(&@end) 76 :@body 77 body 78 :@end 79 %endm 80 81 %macro if_nez(ra, body) 82 %bnez(ra, &@body) 83 %b(&@end) 84 :@body 85 body 86 :@end 87 %endm 88 89 %macro if_ltz(ra, body) 90 %bltz(ra, &@body) 91 %b(&@end) 92 :@body 93 body 94 :@end 95 %endm 96 97 # ---- %ifelse_<cc> ------------------------------------------------------- 98 99 %macro ifelse_eq(ra, rb, tblk, fblk) 100 %beq(ra, rb, &@tblk) 101 fblk 102 %b(&@end) 103 :@tblk 104 tblk 105 :@end 106 %endm 107 108 %macro ifelse_ne(ra, rb, tblk, fblk) 109 %bne(ra, rb, &@tblk) 110 fblk 111 %b(&@end) 112 :@tblk 113 tblk 114 :@end 115 %endm 116 117 %macro ifelse_lt(ra, rb, tblk, fblk) 118 %blt(ra, rb, &@tblk) 119 fblk 120 %b(&@end) 121 :@tblk 122 tblk 123 :@end 124 %endm 125 126 %macro ifelse_ltu(ra, rb, tblk, fblk) 127 %bltu(ra, rb, &@tblk) 128 fblk 129 %b(&@end) 130 :@tblk 131 tblk 132 :@end 133 %endm 134 135 %macro ifelse_eqz(ra, tblk, fblk) 136 %beqz(ra, &@tblk) 137 fblk 138 %b(&@end) 139 :@tblk 140 tblk 141 :@end 142 %endm 143 144 %macro ifelse_nez(ra, tblk, fblk) 145 %bnez(ra, &@tblk) 146 fblk 147 %b(&@end) 148 :@tblk 149 tblk 150 :@end 151 %endm 152 153 %macro ifelse_ltz(ra, tblk, fblk) 154 %bltz(ra, &@tblk) 155 fblk 156 %b(&@end) 157 :@tblk 158 tblk 159 :@end 160 %endm 161 162 # ---- %while_<cc> ------------------------------------------------------- 163 # 164 # Jump-to-test layout: the body runs iff the positive-sense test holds, 165 # and the test is compiled below the body so we only emit a forward 166 # branch at entry. 167 168 %macro while_eq(ra, rb, body) 169 %b(&@test) 170 :@body 171 body 172 :@test 173 %beq(ra, rb, &@body) 174 %endm 175 176 %macro while_ne(ra, rb, body) 177 %b(&@test) 178 :@body 179 body 180 :@test 181 %bne(ra, rb, &@body) 182 %endm 183 184 %macro while_lt(ra, rb, body) 185 %b(&@test) 186 :@body 187 body 188 :@test 189 %blt(ra, rb, &@body) 190 %endm 191 192 %macro while_ltu(ra, rb, body) 193 %b(&@test) 194 :@body 195 body 196 :@test 197 %bltu(ra, rb, &@body) 198 %endm 199 200 %macro while_eqz(ra, body) 201 %b(&@test) 202 :@body 203 body 204 :@test 205 %beqz(ra, &@body) 206 %endm 207 208 %macro while_nez(ra, body) 209 %b(&@test) 210 :@body 211 body 212 :@test 213 %bnez(ra, &@body) 214 %endm 215 216 %macro while_ltz(ra, body) 217 %b(&@test) 218 :@body 219 body 220 :@test 221 %bltz(ra, &@body) 222 %endm 223 224 # ---- %do_while_<cc> ---------------------------------------------------- 225 226 %macro do_while_eq(ra, rb, body) 227 :@body 228 body 229 %beq(ra, rb, &@body) 230 %endm 231 232 %macro do_while_ne(ra, rb, body) 233 :@body 234 body 235 %bne(ra, rb, &@body) 236 %endm 237 238 %macro do_while_lt(ra, rb, body) 239 :@body 240 body 241 %blt(ra, rb, &@body) 242 %endm 243 244 %macro do_while_ltu(ra, rb, body) 245 :@body 246 body 247 %bltu(ra, rb, &@body) 248 %endm 249 250 %macro do_while_eqz(ra, body) 251 :@body 252 body 253 %beqz(ra, &@body) 254 %endm 255 256 %macro do_while_nez(ra, body) 257 :@body 258 body 259 %bnez(ra, &@body) 260 %endm 261 262 %macro do_while_ltz(ra, body) 263 :@body 264 body 265 %bltz(ra, &@body) 266 %endm 267 268 # ---- %for_lt ------------------------------------------------------------ 269 270 %macro for_lt(i_reg, n_reg, body) 271 %li(i_reg, 0) 272 %b(&@test) 273 :@body 274 body 275 %addi(i_reg, i_reg, 1) 276 :@test 277 %blt(i_reg, n_reg, &@body) 278 %endm 279 280 # ---- %loop -------------------------------------------------------------- 281 282 %macro loop(body) 283 :@top 284 body 285 %b(&@top) 286 %endm 287 288 # ---- Tagged loops ------------------------------------------------------- 289 # 290 # Each tagged form emits two global labels `tag_top` and `tag_end`, built 291 # by `##` paste so references cross every macro boundary cleanly. 292 # `%break(tag)` jumps to `tag_end`; `%continue(tag)` jumps to `tag_top`. 293 294 %macro loop_tag(tag, body) 295 : ## tag ## _top 296 body 297 %b(& ## tag ## _top) 298 : ## tag ## _end 299 %endm 300 301 %macro while_tag_eq(tag, ra, rb, body) 302 %b(& ## tag ## _top) 303 :@body 304 body 305 : ## tag ## _top 306 %beq(ra, rb, &@body) 307 : ## tag ## _end 308 %endm 309 310 %macro while_tag_ne(tag, ra, rb, body) 311 %b(& ## tag ## _top) 312 :@body 313 body 314 : ## tag ## _top 315 %bne(ra, rb, &@body) 316 : ## tag ## _end 317 %endm 318 319 %macro while_tag_lt(tag, ra, rb, body) 320 %b(& ## tag ## _top) 321 :@body 322 body 323 : ## tag ## _top 324 %blt(ra, rb, &@body) 325 : ## tag ## _end 326 %endm 327 328 %macro while_tag_ltu(tag, ra, rb, body) 329 %b(& ## tag ## _top) 330 :@body 331 body 332 : ## tag ## _top 333 %bltu(ra, rb, &@body) 334 : ## tag ## _end 335 %endm 336 337 %macro while_tag_eqz(tag, ra, body) 338 %b(& ## tag ## _top) 339 :@body 340 body 341 : ## tag ## _top 342 %beqz(ra, &@body) 343 : ## tag ## _end 344 %endm 345 346 %macro while_tag_nez(tag, ra, body) 347 %b(& ## tag ## _top) 348 :@body 349 body 350 : ## tag ## _top 351 %bnez(ra, &@body) 352 : ## tag ## _end 353 %endm 354 355 %macro while_tag_ltz(tag, ra, body) 356 %b(& ## tag ## _top) 357 :@body 358 body 359 : ## tag ## _top 360 %bltz(ra, &@body) 361 : ## tag ## _end 362 %endm 363 364 %macro for_lt_tag(tag, i_reg, n_reg, body) 365 %li(i_reg, 0) 366 %b(& ## tag ## _test) 367 :@body 368 body 369 : ## tag ## _top 370 %addi(i_reg, i_reg, 1) 371 : ## tag ## _test 372 %blt(i_reg, n_reg, &@body) 373 : ## tag ## _end 374 %endm 375 376 %macro break(tag) 377 %b(& ## tag ## _end) 378 %endm 379 380 %macro continue(tag) 381 %b(& ## tag ## _top) 382 %endm 383 384 # ========================================================================= 385 # %fn -- scope-introducing function definition 386 # ========================================================================= 387 # 388 # Pushes a scope named after the function so `::foo` inside the body 389 # mangles to `name__foo`. The body is bracketed by %enter(size) and 390 # %eret, so functions defined with %fn always carry a standard frame. 391 392 %macro fn(name, size, body) 393 : ## name 394 %scope name 395 %enter(size) 396 body 397 %eret 398 %endscope 399 %endm 400 401 # ========================================================================= 402 # %assert_<cc> macros 403 # ========================================================================= 404 # 405 # Branch past the panic call when the condition holds; otherwise fall 406 # through to `LA a0, msg; LA_BR &panic; CALL`. Each assert requires the 407 # enclosing function to have an established frame. 408 409 %macro assert_eq(ra, rb, msg) 410 %beq(ra, rb, &@done) 411 %la(a0, & ## msg) 412 %call(&panic) 413 :@done 414 %endm 415 416 %macro assert_ne(ra, rb, msg) 417 %bne(ra, rb, &@done) 418 %la(a0, & ## msg) 419 %call(&panic) 420 :@done 421 %endm 422 423 %macro assert_lt(ra, rb, msg) 424 %blt(ra, rb, &@done) 425 %la(a0, & ## msg) 426 %call(&panic) 427 :@done 428 %endm 429 430 %macro assert_ltu(ra, rb, msg) 431 %bltu(ra, rb, &@done) 432 %la(a0, & ## msg) 433 %call(&panic) 434 :@done 435 %endm 436 437 %macro assert_eqz(ra, msg) 438 %beqz(ra, &@done) 439 %la(a0, & ## msg) 440 %call(&panic) 441 :@done 442 %endm 443 444 %macro assert_nez(ra, msg) 445 %bnez(ra, &@done) 446 %la(a0, & ## msg) 447 %call(&panic) 448 :@done 449 %endm 450 451 %macro assert_ltz(ra, msg) 452 %bltz(ra, &@done) 453 %la(a0, & ## msg) 454 %call(&panic) 455 :@done 456 %endm 457 458 # ========================================================================= 459 # Memory and strings 460 # ========================================================================= 461 462 # memcpy(dst=a0, src=a1, n=a2) -> dst (a0) 463 # Leaf. Copies n bytes from src to dst. No overlap support where 464 # dst > src && dst < src + n. 465 :memcpy 466 %scope memcpy 467 %mov(a3, a0) 468 %li(t0, 0) 469 ::loop 470 %beq(t0, a2, &::done) 471 %add(t1, a1, t0) 472 %lb(t1, t1, 0) 473 %add(t2, a3, t0) 474 %sb(t1, t2, 0) 475 %addi(t0, t0, 1) 476 %b(&::loop) 477 ::done 478 %mov(a0, a3) 479 %ret 480 %endscope 481 482 # memset(dst=a0, byte=a1, n=a2) -> dst (a0) 483 :memset 484 %scope memset 485 %mov(a3, a0) 486 %li(t0, 0) 487 ::loop 488 %beq(t0, a2, &::done) 489 %add(t1, a3, t0) 490 %sb(a1, t1, 0) 491 %addi(t0, t0, 1) 492 %b(&::loop) 493 ::done 494 %mov(a0, a3) 495 %ret 496 %endscope 497 498 # memcmp(a=a0, b=a1, n=a2) -> -1/0/1 (a0) 499 :memcmp 500 %scope memcmp 501 %li(t0, 0) 502 ::loop 503 %beq(t0, a2, &::eq) 504 %add(t1, a0, t0) 505 %lb(t1, t1, 0) 506 %add(t2, a1, t0) 507 %lb(t2, t2, 0) 508 %bltu(t1, t2, &::lt) 509 %bltu(t2, t1, &::gt) 510 %addi(t0, t0, 1) 511 %b(&::loop) 512 ::lt 513 %li(a0, -1) 514 %ret 515 ::gt 516 %li(a0, 1) 517 %ret 518 ::eq 519 %li(a0, 0) 520 %ret 521 %endscope 522 523 # strlen(cstr=a0) -> n (a0) 524 :strlen 525 %scope strlen 526 %mov(a1, a0) 527 ::loop 528 %lb(t0, a1, 0) 529 %beqz(t0, &::done) 530 %addi(a1, a1, 1) 531 %b(&::loop) 532 ::done 533 %sub(a0, a1, a0) 534 %ret 535 %endscope 536 537 # streq(a=a0, b=a1) -> 0 or 1 538 :streq 539 %scope streq 540 ::loop 541 %lb(t0, a0, 0) 542 %lb(t1, a1, 0) 543 %bne(t0, t1, &::ne) 544 %beqz(t0, &::eq) 545 %addi(a0, a0, 1) 546 %addi(a1, a1, 1) 547 %b(&::loop) 548 ::ne 549 %li(a0, 0) 550 %ret 551 ::eq 552 %li(a0, 1) 553 %ret 554 %endscope 555 556 # strcmp(a=a0, b=a1) -> -1/0/1 557 :strcmp 558 %scope strcmp 559 ::loop 560 %lb(t0, a0, 0) 561 %lb(t1, a1, 0) 562 %bltu(t0, t1, &::lt) 563 %bltu(t1, t0, &::gt) 564 %beqz(t0, &::eq) 565 %addi(a0, a0, 1) 566 %addi(a1, a1, 1) 567 %b(&::loop) 568 ::lt 569 %li(a0, -1) 570 %ret 571 ::gt 572 %li(a0, 1) 573 %ret 574 ::eq 575 %li(a0, 0) 576 %ret 577 %endscope 578 579 # ========================================================================= 580 # Integer parsing and formatting 581 # ========================================================================= 582 583 # parse_dec(buf=a0, len=a1) -> (value=a0, consumed=a1) 584 # Uses an 8-byte frame slot to save buf_start; all hot-loop state lives 585 # in caller-saved registers. 586 :parse_dec 587 %scope parse_dec 588 %enter(8) 589 %st(a0, sp, 0) 590 %add(a3, a0, a1) 591 %mov(a2, a0) 592 %li(t0, 0) 593 %li(t1, 0) 594 595 %beq(a2, a3, &::after_sign) 596 %lb(t2, a2, 0) 597 %addi(t2, t2, -45) 598 %bnez(t2, &::after_sign) 599 %li(t0, 1) 600 %addi(a2, a2, 1) 601 602 ::after_sign 603 %mov(a1, a2) 604 605 ::digit_loop 606 %beq(a2, a3, &::digits_done) 607 %lb(t2, a2, 0) 608 %addi(t2, t2, -48) 609 %bltz(t2, &::digits_done) 610 %li(a0, 9) 611 %bltu(a0, t2, &::digits_done) 612 %li(a0, 10) 613 %mul(t1, t1, a0) 614 %add(t1, t1, t2) 615 %addi(a2, a2, 1) 616 %b(&::digit_loop) 617 618 ::digits_done 619 %beq(a2, a1, &::no_digits) 620 621 %bnez(t0, &::apply_sign) 622 %b(&::compute_return) 623 ::apply_sign 624 %li(a0, 0) 625 %sub(t1, a0, t1) 626 627 ::compute_return 628 %ld(a0, sp, 0) 629 %sub(a1, a2, a0) 630 %mov(a0, t1) 631 %eret 632 633 ::no_digits 634 %li(a0, 0) 635 %li(a1, 0) 636 %eret 637 %endscope 638 639 # parse_hex(buf=a0, len=a1) -> (value=a0, consumed=a1) 640 :parse_hex 641 %scope parse_hex 642 %enter(8) 643 %st(a0, sp, 0) 644 %add(a3, a0, a1) 645 %mov(a2, a0) 646 %li(t1, 0) 647 %mov(a1, a2) 648 649 ::loop 650 %beq(a2, a3, &::done) 651 %lb(t2, a2, 0) 652 653 %addi(t0, t2, -48) 654 %bltz(t0, &::check_lower) 655 %li(a0, 9) 656 %bltu(a0, t0, &::check_lower) 657 %b(&::accept) 658 659 ::check_lower 660 %addi(t0, t2, -97) 661 %bltz(t0, &::check_upper) 662 %li(a0, 5) 663 %bltu(a0, t0, &::check_upper) 664 %addi(t0, t0, 10) 665 %b(&::accept) 666 667 ::check_upper 668 %addi(t0, t2, -65) 669 %bltz(t0, &::done) 670 %li(a0, 5) 671 %bltu(a0, t0, &::done) 672 %addi(t0, t0, 10) 673 674 ::accept 675 %shli(t1, t1, 4) 676 %or(t1, t1, t0) 677 %addi(a2, a2, 1) 678 %b(&::loop) 679 680 ::done 681 %beq(a2, a1, &::no_digits) 682 %ld(a0, sp, 0) 683 %sub(a1, a2, a0) 684 %mov(a0, t1) 685 %eret 686 687 ::no_digits 688 %li(a0, 0) 689 %li(a1, 0) 690 %eret 691 %endscope 692 693 # fmt_dec(buf=a0, value=a1) -> n_bytes (a0) 694 # 695 # Unified signed formatting: digits are written from the per-iteration 696 # `value % 10`, negated when value is negative. This avoids the 697 # INT_MIN-overflow trap that `value = -value` would hit. 698 :fmt_dec 699 %scope fmt_dec 700 %enter(8) 701 %st(a0, sp, 0) 702 703 %bltz(a1, &::is_neg) 704 %b(&::count) 705 ::is_neg 706 %li(t0, 45) 707 %sb(t0, a0, 0) 708 %addi(a0, a0, 1) 709 710 ::count 711 %mov(t0, a1) 712 %li(a2, 1) 713 %li(t1, 10) 714 ::count_loop 715 %div(t0, t0, t1) 716 %beqz(t0, &::count_done) 717 %addi(a2, a2, 1) 718 %b(&::count_loop) 719 ::count_done 720 721 %add(a3, a0, a2) 722 723 ::dig_loop 724 %addi(a3, a3, -1) 725 %rem(t0, a1, t1) 726 %bltz(t0, &::neg_digit) 727 %b(&::write_digit) 728 ::neg_digit 729 %li(t2, 0) 730 %sub(t0, t2, t0) 731 ::write_digit 732 %addi(t0, t0, 48) 733 %sb(t0, a3, 0) 734 %div(a1, a1, t1) 735 %bnez(a1, &::dig_loop) 736 737 %ld(t2, sp, 0) 738 %add(a0, a0, a2) 739 %sub(a0, a0, t2) 740 %eret 741 %endscope 742 743 # fmt_hex(buf=a0, value=a1) -> n_bytes (a0) 744 :fmt_hex 745 %scope fmt_hex 746 %enter(8) 747 %st(a0, sp, 0) 748 749 %bnez(a1, &::nonzero) 750 %li(t0, 48) 751 %sb(t0, a0, 0) 752 %li(a0, 1) 753 %eret 754 755 ::nonzero 756 %mov(t0, a1) 757 %li(a2, 0) 758 ::count_loop 759 %addi(a2, a2, 1) 760 %shri(t0, t0, 4) 761 %bnez(t0, &::count_loop) 762 763 %add(a3, a0, a2) 764 765 ::dig_loop 766 %addi(a3, a3, -1) 767 %andi(t0, a1, 15) 768 %li(t1, 10) 769 %bltu(t0, t1, &::is_letter) 770 %addi(t0, t0, -10) 771 %addi(t0, t0, 97) 772 %b(&::write_digit) 773 ::is_letter 774 %addi(t0, t0, 48) 775 ::write_digit 776 %sb(t0, a3, 0) 777 %shri(a1, a1, 4) 778 %bnez(a1, &::dig_loop) 779 780 %ld(t2, sp, 0) 781 %add(a0, a0, a2) 782 %sub(a0, a0, t2) 783 %eret 784 %endscope 785 786 # ========================================================================= 787 # Character predicates 788 # ========================================================================= 789 790 # is_digit(c=a0) -> 0 or 1 791 :is_digit 792 %scope is_digit 793 %addi(t0, a0, -48) 794 %li(t1, 10) 795 %li(a0, 1) 796 %bltu(t0, t1, &::done) 797 %li(a0, 0) 798 ::done 799 %ret 800 %endscope 801 802 # is_hex_digit(c=a0) -> 0 or 1 803 :is_hex_digit 804 %scope is_hex_digit 805 %li(t2, 1) 806 %addi(t0, a0, -48) 807 %li(t1, 10) 808 %bltu(t0, t1, &::done) 809 %addi(t0, a0, -97) 810 %li(t1, 6) 811 %bltu(t0, t1, &::done) 812 %addi(t0, a0, -65) 813 %bltu(t0, t1, &::done) 814 %li(t2, 0) 815 ::done 816 %mov(a0, t2) 817 %ret 818 %endscope 819 820 # is_space(c=a0) -> 0 or 1 821 :is_space 822 %scope is_space 823 %li(t2, 1) 824 %addi(t0, a0, -32) 825 %beqz(t0, &::done) 826 %addi(t0, a0, -9) 827 %li(t1, 5) 828 %bltu(t0, t1, &::done) 829 %li(t2, 0) 830 ::done 831 %mov(a0, t2) 832 %ret 833 %endscope 834 835 # is_alpha(c=a0) -> 0 or 1 836 :is_alpha 837 %scope is_alpha 838 %li(t2, 1) 839 %addi(t0, a0, -97) 840 %li(t1, 26) 841 %bltu(t0, t1, &::done) 842 %addi(t0, a0, -65) 843 %bltu(t0, t1, &::done) 844 %li(t2, 0) 845 ::done 846 %mov(a0, t2) 847 %ret 848 %endscope 849 850 # is_alnum(c=a0) -> 0 or 1 851 :is_alnum 852 %scope is_alnum 853 %li(t2, 1) 854 %addi(t0, a0, -48) 855 %li(t1, 10) 856 %bltu(t0, t1, &::done) 857 %addi(t0, a0, -97) 858 %li(t1, 26) 859 %bltu(t0, t1, &::done) 860 %addi(t0, a0, -65) 861 %bltu(t0, t1, &::done) 862 %li(t2, 0) 863 ::done 864 %mov(a0, t2) 865 %ret 866 %endscope 867 868 # ========================================================================= 869 # Raw syscall wrappers 870 # ========================================================================= 871 # 872 # Each wrapper shifts arguments into the syscall convention 873 # (a0 = number, a1..a3/t0/s0/s1 = args 0..5), emits SYSCALL, and returns 874 # the raw kernel result. Syscall clobbers only a0, so t0/s0/s1 do not 875 # need saving. 876 877 # sys_read(fd=a0, buf=a1, len=a2) -> n (a0) 878 :sys_read 879 %mov(a3, a2) 880 %mov(a2, a1) 881 %mov(a1, a0) 882 %li(a0, %p1_sys_read) 883 %syscall 884 %ret 885 886 # sys_write(fd=a0, buf=a1, len=a2) -> n (a0) 887 :sys_write 888 %mov(a3, a2) 889 %mov(a2, a1) 890 %mov(a1, a0) 891 %li(a0, %p1_sys_write) 892 %syscall 893 %ret 894 895 # sys_open(path=a0, flags=a1, mode=a2) -> fd (a0) 896 # Implemented as openat(AT_FDCWD, path, flags, mode). AT_FDCWD = -100. 897 :sys_open 898 %mov(t0, a2) 899 %mov(a3, a1) 900 %mov(a2, a0) 901 %li(a1, -100) 902 %li(a0, %p1_sys_openat) 903 %syscall 904 %ret 905 906 # sys_close(fd=a0) -> r (a0) 907 :sys_close 908 %mov(a1, a0) 909 %li(a0, %p1_sys_close) 910 %syscall 911 %ret 912 913 # sys_exit(code=a0) -> never returns 914 :sys_exit 915 %scope sys_exit 916 %mov(a1, a0) 917 %li(a0, %p1_sys_exit) 918 %syscall 919 ::spin 920 %b(&::spin) 921 %endscope 922 923 # ========================================================================= 924 # Print helpers 925 # ========================================================================= 926 # 927 # print(buf, len) and eprint(buf, len) loop on sys_write until all bytes 928 # are written or the kernel reports an error. All other print helpers 929 # compose on top of those two. 930 931 %fn(print, 16, { 932 %st(s0, sp, 0) 933 %st(s1, sp, 8) 934 %mov(s0, a0) 935 %mov(s1, a1) 936 937 ::loop 938 %beqz(s1, &::done_ok) 939 %li(a0, 1) 940 %mov(a1, s0) 941 %mov(a2, s1) 942 %call(&sys_write) 943 %bltz(a0, &::done) 944 %add(s0, s0, a0) 945 %sub(s1, s1, a0) 946 %b(&::loop) 947 948 ::done_ok 949 %li(a0, 0) 950 ::done 951 %ld(s0, sp, 0) 952 %ld(s1, sp, 8) 953 }) 954 955 %fn(eprint, 16, { 956 %st(s0, sp, 0) 957 %st(s1, sp, 8) 958 %mov(s0, a0) 959 %mov(s1, a1) 960 961 ::loop 962 %beqz(s1, &::done_ok) 963 %li(a0, 2) 964 %mov(a1, s0) 965 %mov(a2, s1) 966 %call(&sys_write) 967 %bltz(a0, &::done) 968 %add(s0, s0, a0) 969 %sub(s1, s1, a0) 970 %b(&::loop) 971 972 ::done_ok 973 %li(a0, 0) 974 ::done 975 %ld(s0, sp, 0) 976 %ld(s1, sp, 8) 977 }) 978 979 %fn(println, 16, { 980 %st(s0, sp, 0) 981 982 %call(&print) 983 %mov(s0, a0) 984 %bltz(s0, &::done) 985 986 %la(a0, &libp1pp__newline) 987 %li(a1, 1) 988 %call(&print) 989 %mov(s0, a0) 990 991 ::done 992 %mov(a0, s0) 993 %ld(s0, sp, 0) 994 }) 995 996 %fn(eprintln, 16, { 997 %st(s0, sp, 0) 998 999 %call(&eprint) 1000 %mov(s0, a0) 1001 %bltz(s0, &::done) 1002 1003 %la(a0, &libp1pp__newline) 1004 %li(a1, 1) 1005 %call(&eprint) 1006 %mov(s0, a0) 1007 1008 ::done 1009 %mov(a0, s0) 1010 %ld(s0, sp, 0) 1011 }) 1012 1013 %fn(print_cstr, 16, { 1014 %st(s0, sp, 0) 1015 %mov(s0, a0) 1016 %call(&strlen) 1017 %mov(a1, a0) 1018 %mov(a0, s0) 1019 %call(&print) 1020 %ld(s0, sp, 0) 1021 }) 1022 1023 %fn(eprint_cstr, 16, { 1024 %st(s0, sp, 0) 1025 %mov(s0, a0) 1026 %call(&strlen) 1027 %mov(a1, a0) 1028 %mov(a0, s0) 1029 %call(&eprint) 1030 %ld(s0, sp, 0) 1031 }) 1032 1033 %fn(print_int, 0, { 1034 %mov(a1, a0) 1035 %la(a0, &libp1pp__num_buf) 1036 %call(&fmt_dec) 1037 %mov(a1, a0) 1038 %la(a0, &libp1pp__num_buf) 1039 %call(&print) 1040 }) 1041 1042 %fn(print_hex, 0, { 1043 %mov(a1, a0) 1044 %la(a0, &libp1pp__num_buf) 1045 %call(&fmt_hex) 1046 %mov(a1, a0) 1047 %la(a0, &libp1pp__num_buf) 1048 %call(&print) 1049 }) 1050 1051 # ========================================================================= 1052 # File helpers 1053 # ========================================================================= 1054 1055 # read_file(path=a0, buf=a1, cap=a2) -> n or -1 1056 %fn(read_file, 32, { 1057 %st(s0, sp, 0) 1058 %st(s1, sp, 8) 1059 %st(s2, sp, 16) 1060 %st(s3, sp, 24) 1061 1062 %mov(s1, a1) 1063 %mov(s2, a2) 1064 1065 %li(a1, 0) 1066 %li(a2, 0) 1067 %call(&sys_open) 1068 %bltz(a0, &::open_fail) 1069 %mov(s3, a0) 1070 1071 %mov(a0, s3) 1072 %mov(a1, s1) 1073 %mov(a2, s2) 1074 %call(&sys_read) 1075 %mov(s0, a0) 1076 1077 %mov(a0, s3) 1078 %call(&sys_close) 1079 1080 %mov(a0, s0) 1081 %bltz(a0, &::read_fail) 1082 %b(&::done) 1083 1084 ::read_fail 1085 %li(a0, -1) 1086 %b(&::done) 1087 1088 ::open_fail 1089 %li(a0, -1) 1090 1091 ::done 1092 %ld(s0, sp, 0) 1093 %ld(s1, sp, 8) 1094 %ld(s2, sp, 16) 1095 %ld(s3, sp, 24) 1096 }) 1097 1098 # libp1pp__write_all(fd=a0, buf=a1, len=a2) -> 0 or <0 on error 1099 # 1100 # Loop on sys_write until all bytes are written. Used by print / eprint 1101 # / write_file. Retries partial writes but returns the first negative 1102 # kernel return unchanged. 1103 %fn(libp1pp__write_all, 24, { 1104 %st(s0, sp, 0) 1105 %st(s1, sp, 8) 1106 %st(s2, sp, 16) 1107 1108 %mov(s0, a0) 1109 %mov(s1, a1) 1110 %mov(s2, a2) 1111 1112 ::loop 1113 %beqz(s2, &::done_ok) 1114 %mov(a0, s0) 1115 %mov(a1, s1) 1116 %mov(a2, s2) 1117 %call(&sys_write) 1118 %bltz(a0, &::done) 1119 %add(s1, s1, a0) 1120 %sub(s2, s2, a0) 1121 %b(&::loop) 1122 1123 ::done_ok 1124 %li(a0, 0) 1125 ::done 1126 %ld(s0, sp, 0) 1127 %ld(s1, sp, 8) 1128 %ld(s2, sp, 16) 1129 }) 1130 1131 # write_file(path=a0, buf=a1, len=a2) -> 0 or -1 1132 # 1133 # Flags: O_WRONLY|O_CREAT|O_TRUNC. On Linux these are 0x1 | 0x40 | 1134 # 0x200 = 0x241. Mode 0644 octal = 0x1A4. 1135 %fn(write_file, 24, { 1136 %st(s0, sp, 0) 1137 %st(s1, sp, 8) 1138 %st(s2, sp, 16) 1139 1140 %mov(s0, a1) 1141 %mov(s1, a2) 1142 1143 %li(a1, 0x241) 1144 %li(a2, 0x1A4) 1145 %call(&sys_open) 1146 %bltz(a0, &::open_fail) 1147 %mov(s2, a0) 1148 1149 %mov(a0, s2) 1150 %mov(a1, s0) 1151 %mov(a2, s1) 1152 %call(&libp1pp__write_all) 1153 1154 %mov(s0, a0) 1155 %mov(a0, s2) 1156 %call(&sys_close) 1157 1158 %mov(a0, s0) 1159 %bltz(a0, &::fail_ret) 1160 %li(a0, 0) 1161 %b(&::done) 1162 1163 ::fail_ret 1164 %li(a0, -1) 1165 %b(&::done) 1166 1167 ::open_fail 1168 %li(a0, -1) 1169 1170 ::done 1171 %ld(s0, sp, 0) 1172 %ld(s1, sp, 8) 1173 %ld(s2, sp, 16) 1174 }) 1175 1176 # ========================================================================= 1177 # Bump allocator 1178 # ========================================================================= 1179 # 1180 # Single global arena, bytes carved by monotonic cursor with 8-byte 1181 # alignment. bump_alloc returns 0 when the request would overflow. 1182 1183 # bump_init(base=a0, cap=a1) -> 0 1184 :bump_init 1185 %la(t0, &libp1pp__bump_base) 1186 %st(a0, t0, 0) 1187 %la(t0, &libp1pp__bump_cursor) 1188 %st(a0, t0, 0) 1189 %la(t0, &libp1pp__bump_cap) 1190 %st(a1, t0, 0) 1191 %li(a0, 0) 1192 %ret 1193 1194 # bump_alloc(n=a0) -> ptr (0 on exhaustion) 1195 # 1196 # Round n up to a multiple of 8, then admit iff cursor + n_rounded does 1197 # not exceed base + cap. On success, advance the cursor and return the 1198 # pre-advance value; on failure, leave the cursor untouched and return 0. 1199 :bump_alloc 1200 %scope bump_alloc 1201 %addi(a0, a0, 7) 1202 %li(t0, -8) 1203 %and(a0, a0, t0) 1204 %la(t0, &libp1pp__bump_cursor) 1205 %ld(t1, t0, 0) 1206 %add(t2, t1, a0) 1207 %la(a1, &libp1pp__bump_base) 1208 %ld(a2, a1, 0) 1209 %la(a1, &libp1pp__bump_cap) 1210 %ld(a3, a1, 0) 1211 %add(a3, a2, a3) 1212 %bltu(a3, t2, &::fail) 1213 %st(t2, t0, 0) 1214 %mov(a0, t1) 1215 %ret 1216 ::fail 1217 %li(a0, 0) 1218 %ret 1219 %endscope 1220 1221 # bump_mark() -> saved 1222 :bump_mark 1223 %la(t0, &libp1pp__bump_cursor) 1224 %ld(a0, t0, 0) 1225 %ret 1226 1227 # bump_release(saved=a0) -> 0 1228 :bump_release 1229 %la(t0, &libp1pp__bump_cursor) 1230 %st(a0, t0, 0) 1231 %li(a0, 0) 1232 %ret 1233 1234 # bump_reset() -> 0 1235 :bump_reset 1236 %la(t0, &libp1pp__bump_base) 1237 %ld(t1, t0, 0) 1238 %la(t0, &libp1pp__bump_cursor) 1239 %st(t1, t0, 0) 1240 %li(a0, 0) 1241 %ret 1242 1243 # ========================================================================= 1244 # Panic 1245 # ========================================================================= 1246 1247 # panic(msg_cstr=a0) -> never returns 1248 %fn(panic, 0, { 1249 %call(&eprint_cstr) 1250 %la(a0, &libp1pp__newline) 1251 %li(a1, 1) 1252 %call(&eprint) 1253 %li(a0, 1) 1254 %call(&sys_exit) 1255 ::spin 1256 %b(&::spin) 1257 }) 1258 1259 # ========================================================================= 1260 # Internal data 1261 # ========================================================================= 1262 1263 # Single newline byte for println / eprintln / panic. Emitted as an 1264 # 8-byte word (0x0A in the low byte, zeros above) so the following 1265 # buffers and the user source that comes after libp1pp stay 8-byte 1266 # aligned. sys_write reads only the one byte callers request. 1267 :libp1pp__newline 1268 $(10) 1269 1270 # Scratch buffer used by print_int / print_hex. fmt_dec writes at most 1271 # 20 bytes, fmt_hex at most 16, so 32 bytes with word alignment is 1272 # comfortably above both. 1273 :libp1pp__num_buf 1274 $(0) $(0) $(0) $(0) 1275 1276 # Bump-allocator state. Zero-initialized so bump_alloc returns 0 until 1277 # bump_init installs an arena. 1278 :libp1pp__bump_base 1279 $(0) 1280 :libp1pp__bump_cursor 1281 $(0) 1282 :libp1pp__bump_cap 1283 $(0)