boot2

Playing with the boostrap
git clone https://git.ryansepassi.com/git/boot2.git
Log | Files | Refs | README

P1-riscv32.M1pp (14570B)


      1 # P1-riscv32.M1pp -- P1 RV32IM backend expressed in m1macro.
      2 #
      3 # Mirrors p1/P1-aarch64.M1pp; same macro surface, different encodings.
      4 # Native register picks follow docs/P1.md's RISC-V mapping table.
      5 #
      6 # Hidden backend regs:
      7 #   br      = t6 (x31)  -- dedicated branch-target mechanism
      8 #   scratch = t5 (x30)  -- per-expansion scratch, never live across ops
      9 #   save0   = t4 (x29)  -- transient across SYSCALL only
     10 #   save1   = t3 (x28)
     11 #   save2   = a6 (x16)
     12 #   saved_fp = fp (x8)  -- used by ENTER/ERET to capture caller sp
     13 #   a7      = x17       -- Linux RISC-V syscall-number slot
     14 #   a4      = x14       -- syscall arg4 slot
     15 #   a5      = x15       -- syscall arg5 slot
     16 
     17 # Width hooks consumed by the portable P1pp support library.
     18 %macro p1_word_bytes()
     19 4
     20 %endm
     21 %macro p1_word_bits()
     22 32
     23 %endm
     24 %macro p1_word_shift()
     25 2
     26 %endm
     27 %macro p1_scheme_heap_bytes()
     28 0x00F00000
     29 %endm
     30 %macro p1_waitid_status_off()
     31 20
     32 %endm
     33 
     34 # ---- Native register numbers --------------------------------------------
     35 
     36 %macro rv_reg_a0()
     37 10
     38 %endm
     39 %macro rv_reg_a1()
     40 11
     41 %endm
     42 %macro rv_reg_a2()
     43 12
     44 %endm
     45 %macro rv_reg_a3()
     46 13
     47 %endm
     48 %macro rv_reg_a4()
     49 14
     50 %endm
     51 %macro rv_reg_a5()
     52 15
     53 %endm
     54 %macro rv_reg_a6()
     55 16
     56 %endm
     57 %macro rv_reg_a7()
     58 17
     59 %endm
     60 %macro rv_reg_t0()
     61 5
     62 %endm
     63 %macro rv_reg_t1()
     64 6
     65 %endm
     66 %macro rv_reg_t2()
     67 7
     68 %endm
     69 %macro rv_reg_s0()
     70 9
     71 %endm
     72 %macro rv_reg_s1()
     73 18
     74 %endm
     75 %macro rv_reg_s2()
     76 19
     77 %endm
     78 %macro rv_reg_s3()
     79 20
     80 %endm
     81 %macro rv_reg_sp()
     82 2
     83 %endm
     84 %macro rv_reg_zero()
     85 0
     86 %endm
     87 %macro rv_reg_ra()
     88 1
     89 %endm
     90 %macro rv_reg_fp()
     91 8
     92 %endm
     93 %macro rv_reg_br()
     94 31
     95 %endm
     96 %macro rv_reg_scratch()
     97 30
     98 %endm
     99 %macro rv_reg_save0()
    100 29
    101 %endm
    102 %macro rv_reg_save1()
    103 28
    104 %endm
    105 %macro rv_reg_save2()
    106 16
    107 %endm
    108 
    109 %macro rv_reg(r)
    110 %rv_reg_##r
    111 %endm
    112 
    113 %macro rv_is_sp_a0()
    114 0
    115 %endm
    116 %macro rv_is_sp_a1()
    117 0
    118 %endm
    119 %macro rv_is_sp_a2()
    120 0
    121 %endm
    122 %macro rv_is_sp_a3()
    123 0
    124 %endm
    125 %macro rv_is_sp_a4()
    126 0
    127 %endm
    128 %macro rv_is_sp_a5()
    129 0
    130 %endm
    131 %macro rv_is_sp_a6()
    132 0
    133 %endm
    134 %macro rv_is_sp_a7()
    135 0
    136 %endm
    137 %macro rv_is_sp_t0()
    138 0
    139 %endm
    140 %macro rv_is_sp_t1()
    141 0
    142 %endm
    143 %macro rv_is_sp_t2()
    144 0
    145 %endm
    146 %macro rv_is_sp_s0()
    147 0
    148 %endm
    149 %macro rv_is_sp_s1()
    150 0
    151 %endm
    152 %macro rv_is_sp_s2()
    153 0
    154 %endm
    155 %macro rv_is_sp_s3()
    156 0
    157 %endm
    158 %macro rv_is_sp_sp()
    159 1
    160 %endm
    161 %macro rv_is_sp_zero()
    162 0
    163 %endm
    164 %macro rv_is_sp_ra()
    165 0
    166 %endm
    167 %macro rv_is_sp_fp()
    168 0
    169 %endm
    170 %macro rv_is_sp_br()
    171 0
    172 %endm
    173 %macro rv_is_sp_scratch()
    174 0
    175 %endm
    176 %macro rv_is_sp_save0()
    177 0
    178 %endm
    179 %macro rv_is_sp_save1()
    180 0
    181 %endm
    182 %macro rv_is_sp_save2()
    183 0
    184 %endm
    185 
    186 %macro rv_is_sp(r)
    187 %rv_is_sp_##r
    188 %endm
    189 
    190 # ---- Low-level instruction encoders --------------------------------------
    191 
    192 # R-type: funct7[31:25] rs2[24:20] rs1[19:15] funct3[14:12] rd[11:7] opcode[6:0]
    193 %macro rv_r_type(base, rd, ra, rb)
    194 %((| base (<< %rv_reg(rb) 20) (<< %rv_reg(ra) 15) (<< %rv_reg(rd) 7)))
    195 %endm
    196 
    197 # I-type: imm[31:20] rs1[19:15] funct3[14:12] rd[11:7] opcode[6:0]
    198 %macro rv_i_type(base, rd, ra, imm12)
    199 %((| base (<< (& imm12 0xFFF) 20) (<< %rv_reg(ra) 15) (<< %rv_reg(rd) 7)))
    200 %endm
    201 
    202 # S-type: imm[31:25] rs2[24:20] rs1[19:15] funct3[14:12] imm[11:7] opcode[6:0]
    203 %macro rv_s_type(base, rs, ra, imm12)
    204 %((| base (<< (& (>> imm12 5) 0x7F) 25) (<< %rv_reg(rs) 20) (<< %rv_reg(ra) 15) (<< (& imm12 0x1F) 7)))
    205 %endm
    206 
    207 # B-type: imm[12|10:5] rs2 rs1 funct3 imm[4:1|11] opcode. 12-bit signed,
    208 # imm[0] always 0. For the hardcoded skip-over-jalr we only need a fixed
    209 # positive offset (8 bytes = 2 insns), so inline the resulting bit pattern.
    210 %macro rv_b_type_skip8(base, ra, rb)
    211 # imm value 8 -> imm[11:0] = 0000_0000_0100. Bits of encoded imm:
    212 #   imm[12]=0, imm[10:5]=0, imm[4:1]=0100 (=4), imm[11]=0.
    213 #   encoded bits: [31:25]=0, [11:7]= (imm[4:1] << 1) | imm[11] = (4<<1)|0 = 8.
    214 %((| base (<< %rv_reg(rb) 20) (<< %rv_reg(ra) 15) (<< 8 7)))
    215 %endm
    216 
    217 %macro rv_addi(rd, ra, imm12)
    218 %rv_i_type(0x00000013, rd, ra, imm12)
    219 %endm
    220 
    221 # rv_addi with arbitrary 32-bit signed immediate. Falls back to a
    222 # word literal load into `scratch` followed by an R-type ADD when the
    223 # immediate doesn't fit in ADDI's 12-bit signed field. `scratch` (t5/x30)
    224 # is per-expansion and never live across ops, so clobbering it is safe.
    225 %macro rv_addi_any(rd, ra, imm)
    226 %select((>= imm -2048),
    227     %select((<= imm 2047),
    228         %rv_addi(rd, ra, imm),
    229         %rv_lit32_prefix(scratch)
    230         %(imm)
    231         %rv_r_type(0x00000033, rd, ra, scratch)),
    232     %rv_lit32_prefix(scratch)
    233     %(imm)
    234     %rv_r_type(0x00000033, rd, ra, scratch))
    235 %endm
    236 
    237 %macro rv_lw(rd, ra, imm12)
    238 %rv_i_type(0x00002003, rd, ra, imm12)
    239 %endm
    240 
    241 %macro rv_sw(rs, ra, imm12)
    242 %rv_s_type(0x00002023, rs, ra, imm12)
    243 %endm
    244 
    245 %macro rv_lbu(rd, ra, imm12)
    246 %rv_i_type(0x00004003, rd, ra, imm12)
    247 %endm
    248 
    249 %macro rv_sb(rs, ra, imm12)
    250 %rv_s_type(0x00000023, rs, ra, imm12)
    251 %endm
    252 
    253 # Load/store with arbitrary signed offset. The native I-type/S-type
    254 # imm12 covers [-2048, 2047]; past that, materialize the offset in
    255 # scratch (t5/x30), compute scratch = ra + scratch via R-type ADD, and
    256 # issue the load/store with offset 0. Callers must not pass scratch as
    257 # `ra` or `rs` — the materialize would clobber it before the address
    258 # computation reads it.
    259 %macro rv_lw_any(rd, ra, off)
    260 %select((>= off -2048),
    261     %select((<= off 2047),
    262         %rv_lw(rd, ra, off),
    263         %rv_lit32_prefix(scratch)
    264         %(off)
    265         %rv_r_type(0x00000033, scratch, ra, scratch)
    266         %rv_lw(rd, scratch, 0)),
    267     %rv_lit32_prefix(scratch)
    268     %(off)
    269     %rv_r_type(0x00000033, scratch, ra, scratch)
    270     %rv_lw(rd, scratch, 0))
    271 %endm
    272 
    273 %macro rv_sw_any(rs, ra, off)
    274 %select((>= off -2048),
    275     %select((<= off 2047),
    276         %rv_sw(rs, ra, off),
    277         %rv_lit32_prefix(scratch)
    278         %(off)
    279         %rv_r_type(0x00000033, scratch, ra, scratch)
    280         %rv_sw(rs, scratch, 0)),
    281     %rv_lit32_prefix(scratch)
    282     %(off)
    283     %rv_r_type(0x00000033, scratch, ra, scratch)
    284     %rv_sw(rs, scratch, 0))
    285 %endm
    286 
    287 %macro rv_lbu_any(rd, ra, off)
    288 %select((>= off -2048),
    289     %select((<= off 2047),
    290         %rv_lbu(rd, ra, off),
    291         %rv_lit32_prefix(scratch)
    292         %(off)
    293         %rv_r_type(0x00000033, scratch, ra, scratch)
    294         %rv_lbu(rd, scratch, 0)),
    295     %rv_lit32_prefix(scratch)
    296     %(off)
    297     %rv_r_type(0x00000033, scratch, ra, scratch)
    298     %rv_lbu(rd, scratch, 0))
    299 %endm
    300 
    301 %macro rv_sb_any(rs, ra, off)
    302 %select((>= off -2048),
    303     %select((<= off 2047),
    304         %rv_sb(rs, ra, off),
    305         %rv_lit32_prefix(scratch)
    306         %(off)
    307         %rv_r_type(0x00000033, scratch, ra, scratch)
    308         %rv_sb(rs, scratch, 0)),
    309     %rv_lit32_prefix(scratch)
    310     %(off)
    311     %rv_r_type(0x00000033, scratch, ra, scratch)
    312     %rv_sb(rs, scratch, 0))
    313 %endm
    314 
    315 %macro rv_mov_rr(dst, src)
    316 %rv_addi(dst, src, 0)
    317 %endm
    318 
    319 %macro rv_slli(rd, ra, shamt)
    320 %((| 0x00001013 (<< (& shamt 0x1F) 20) (<< %rv_reg(ra) 15) (<< %rv_reg(rd) 7)))
    321 %endm
    322 
    323 %macro rv_srli(rd, ra, shamt)
    324 %((| 0x00005013 (<< (& shamt 0x1F) 20) (<< %rv_reg(ra) 15) (<< %rv_reg(rd) 7)))
    325 %endm
    326 
    327 %macro rv_srai(rd, ra, shamt)
    328 %((| 0x40005013 (<< (& shamt 0x1F) 20) (<< %rv_reg(ra) 15) (<< %rv_reg(rd) 7)))
    329 %endm
    330 
    331 %macro rv_jalr(rd, rs, imm12)
    332 %((| 0x00000067 (<< (& imm12 0xFFF) 20) (<< %rv_reg(rs) 15) (<< %rv_reg(rd) 7)))
    333 %endm
    334 
    335 %macro rv_ecall()
    336 %(0x00000073)
    337 %endm
    338 
    339 # 32-bit word/address literal-pool prefix:
    340 #   auipc rd, 0          pc-relative base
    341 #   lw    rd, 12(rd)     load the 4-byte literal from pc+12
    342 #   jal   x0, 8          skip the 4-byte literal.
    343 %macro rv_lit32_prefix(rd)
    344 %((| 0x00000017 (<< %rv_reg(rd) 7)))
    345 %((| 0x00C02003 (<< %rv_reg(rd) 15) (<< %rv_reg(rd) 7)))
    346 %(0x0080006F)
    347 %endm
    348 
    349 # Memory op fallback: offset outside signed 12-bit range. Load the
    350 # offset into `scratch` via LUI+ADDI dance? For stage0 programs the
    351 # curated offsets stay inside -2048..2047, so fall back is unused;
    352 # still emit a defensive failure to flag any future overflow.
    353 # (In practice none of the LD/ST off values in p1_gen.py exceed the
    354 # signed 12-bit range, so no fallback path is wired in here.)
    355 
    356 # ---- P1 register-register op lowering -----------------------------------
    357 
    358 %macro rv_rrr_ADD(rd, ra, rb)
    359 %rv_r_type(0x00000033, rd, ra, rb)
    360 %endm
    361 %macro rv_rrr_SUB(rd, ra, rb)
    362 %rv_r_type(0x40000033, rd, ra, rb)
    363 %endm
    364 %macro rv_rrr_AND(rd, ra, rb)
    365 %rv_r_type(0x00007033, rd, ra, rb)
    366 %endm
    367 %macro rv_rrr_OR(rd, ra, rb)
    368 %rv_r_type(0x00006033, rd, ra, rb)
    369 %endm
    370 %macro rv_rrr_XOR(rd, ra, rb)
    371 %rv_r_type(0x00004033, rd, ra, rb)
    372 %endm
    373 %macro rv_rrr_SHL(rd, ra, rb)
    374 %rv_r_type(0x00001033, rd, ra, rb)
    375 %endm
    376 %macro rv_rrr_SHR(rd, ra, rb)
    377 %rv_r_type(0x00005033, rd, ra, rb)
    378 %endm
    379 %macro rv_rrr_SAR(rd, ra, rb)
    380 %rv_r_type(0x40005033, rd, ra, rb)
    381 %endm
    382 %macro rv_rrr_MUL(rd, ra, rb)
    383 %rv_r_type(0x02000033, rd, ra, rb)
    384 %endm
    385 %macro rv_rrr_DIV(rd, ra, rb)
    386 %rv_r_type(0x02004033, rd, ra, rb)
    387 %endm
    388 %macro rv_rrr_UDIV(rd, ra, rb)
    389 %rv_r_type(0x02005033, rd, ra, rb)
    390 %endm
    391 %macro rv_rrr_REM(rd, ra, rb)
    392 %rv_r_type(0x02006033, rd, ra, rb)
    393 %endm
    394 %macro rv_rrr_UREM(rd, ra, rb)
    395 %rv_r_type(0x02007033, rd, ra, rb)
    396 %endm
    397 
    398 %macro rv_rrr_op(op, rd, ra, rb)
    399 %rv_rrr_##op(rd, ra, rb)
    400 %endm
    401 
    402 # ---- P1 operation lowering -----------------------------------------------
    403 
    404 %macro p1_li(rd, imm)
    405 %rv_lit32_prefix(rd)
    406 %(imm)
    407 %endm
    408 
    409 %macro p1_la(rd)
    410 %rv_lit32_prefix(rd)
    411 %endm
    412 
    413 %macro p1_labr()
    414 %rv_lit32_prefix(br)
    415 %endm
    416 
    417 %macro p1_mov(rd, rs)
    418 %select((= %rv_is_sp(rs) 1),
    419     %rv_addi(rd, sp, 8),
    420     %rv_mov_rr(rd, rs))
    421 %endm
    422 
    423 %macro p1_rrr(op, rd, ra, rb)
    424 %rv_rrr_op(op, rd, ra, rb)
    425 %endm
    426 
    427 %macro p1_addi(rd, ra, imm)
    428 %rv_addi_any(rd, ra, imm)
    429 %endm
    430 
    431 # Logical-immediate fallback: when imm fits the I-type's 12-bit signed
    432 # field, emit the native ANDI/ORI; otherwise materialize the immediate
    433 # in scratch (t5/x30) and use the R-type AND/OR. funct3=7 (AND) or 6
    434 # (OR) is shared between the I-type (opcode 0x13) and R-type
    435 # (opcode 0x33) encodings.
    436 %macro rv_logi_any(rd, ra, imm, base_i, base_r)
    437 %select((>= imm -2048),
    438     %select((<= imm 2047),
    439         %rv_i_type(base_i, rd, ra, imm),
    440         %rv_lit32_prefix(scratch)
    441         %(imm)
    442         %rv_r_type(base_r, rd, ra, scratch)),
    443     %rv_lit32_prefix(scratch)
    444     %(imm)
    445     %rv_r_type(base_r, rd, ra, scratch))
    446 %endm
    447 
    448 %macro p1_logi_ANDI(rd, ra, imm)
    449 %rv_logi_any(rd, ra, imm, 0x00007013, 0x00007033)
    450 %endm
    451 %macro p1_logi_ORI(rd, ra, imm)
    452 %rv_logi_any(rd, ra, imm, 0x00006013, 0x00006033)
    453 %endm
    454 %macro p1_logi(op, rd, ra, imm)
    455 %p1_logi_##op(rd, ra, imm)
    456 %endm
    457 
    458 %macro p1_shifti_SHLI(rd, ra, imm)
    459 %rv_slli(rd, ra, imm)
    460 %endm
    461 %macro p1_shifti_SHRI(rd, ra, imm)
    462 %rv_srli(rd, ra, imm)
    463 %endm
    464 %macro p1_shifti_SARI(rd, ra, imm)
    465 %rv_srai(rd, ra, imm)
    466 %endm
    467 %macro p1_shifti(op, rd, ra, imm)
    468 %p1_shifti_##op(rd, ra, imm)
    469 %endm
    470 
    471 %macro p1_mem_LD(rt, rn, off)
    472 %rv_lw_any(rt, rn, off)
    473 %endm
    474 %macro p1_mem_ST(rt, rn, off)
    475 %rv_sw_any(rt, rn, off)
    476 %endm
    477 %macro p1_mem_LB(rt, rn, off)
    478 %rv_lbu_any(rt, rn, off)
    479 %endm
    480 %macro p1_mem_SB(rt, rn, off)
    481 %rv_sb_any(rt, rn, off)
    482 %endm
    483 %macro p1_mem(op, rt, rn, off)
    484 %select((= %rv_is_sp(rn) 1),
    485     %p1_mem_##op(rt, rn, (+ off 8)),
    486     %p1_mem_##op(rt, rn, off))
    487 %endm
    488 
    489 %macro p1_ldarg(rd, slot)
    490 %rv_lw(rd, sp, 4)
    491 %rv_lw_any(rd, rd, (+ 8 (* 4 slot)))
    492 %endm
    493 
    494 %macro p1_b()
    495 %rv_jalr(zero, br, 0)
    496 %endm
    497 
    498 %macro p1_br(rs)
    499 %rv_jalr(zero, rs, 0)
    500 %endm
    501 
    502 %macro p1_call()
    503 %rv_jalr(ra, br, 0)
    504 %endm
    505 
    506 %macro p1_callr(rs)
    507 %rv_jalr(ra, rs, 0)
    508 %endm
    509 
    510 %macro p1_ret()
    511 %rv_jalr(zero, ra, 0)
    512 %endm
    513 
    514 %macro p1_eret()
    515 %rv_lw(ra, sp, 0)
    516 %rv_lw(fp, sp, 4)
    517 %rv_mov_rr(sp, fp)
    518 %rv_jalr(zero, ra, 0)
    519 %endm
    520 
    521 %macro p1_tail()
    522 %rv_lw(ra, sp, 0)
    523 %rv_lw(fp, sp, 4)
    524 %rv_mov_rr(sp, fp)
    525 %rv_jalr(zero, br, 0)
    526 %endm
    527 
    528 %macro p1_tailr(rs)
    529 %rv_lw(ra, sp, 0)
    530 %rv_lw(fp, sp, 4)
    531 %rv_mov_rr(sp, fp)
    532 %rv_jalr(zero, rs, 0)
    533 %endm
    534 
    535 # Conditional branch: emit a skip-taken native branch over the `%p1_b`
    536 # fall-through, then the jalr(br) that takes the P1 branch. Each native
    537 # B-type here uses the inverted condition with a +8 offset so the `jalr`
    538 # two insns below is the taken target.
    539 %macro p1_condb_BEQ(ra, rb)
    540 %rv_b_type_skip8(0x00001063, ra, rb)
    541 %p1_b
    542 %endm
    543 %macro p1_condb_BNE(ra, rb)
    544 %rv_b_type_skip8(0x00000063, ra, rb)
    545 %p1_b
    546 %endm
    547 %macro p1_condb_BLT(ra, rb)
    548 %rv_b_type_skip8(0x00005063, ra, rb)
    549 %p1_b
    550 %endm
    551 %macro p1_condb_BLTU(ra, rb)
    552 %rv_b_type_skip8(0x00007063, ra, rb)
    553 %p1_b
    554 %endm
    555 %macro p1_condb(op, ra, rb)
    556 %p1_condb_##op(ra, rb)
    557 %endm
    558 
    559 %macro p1_condbz_BEQZ(ra)
    560 %rv_b_type_skip8(0x00001063, ra, zero)
    561 %p1_b
    562 %endm
    563 %macro p1_condbz_BNEZ(ra)
    564 %rv_b_type_skip8(0x00000063, ra, zero)
    565 %p1_b
    566 %endm
    567 %macro p1_condbz_BLTZ(ra)
    568 %rv_b_type_skip8(0x00005063, ra, zero)
    569 %p1_b
    570 %endm
    571 %macro p1_condbz(op, ra)
    572 %p1_condbz_##op(ra)
    573 %endm
    574 
    575 %macro p1_enter(size)
    576 %rv_addi_any(sp, sp, (- 0 (& (+ (+ 8 size) 15) -16)))
    577 %rv_sw(ra, sp, 0)
    578 %rv_addi_any(fp, sp, (& (+ (+ 8 size) 15) -16))
    579 %rv_sw(fp, sp, 4)
    580 %endm
    581 
    582 %macro p1_entry()
    583 # :_start stub per the P1 program-entry model. Linux RV32 puts argc
    584 # at [sp] and argv starting at [sp+4], matching the generic SysV entry
    585 # stack. Load argc into a0, compute &argv[0] into a1, call p1_main under
    586 # the one-word direct-result convention, then issue sys_exit with the
    587 # returned status.
    588 :_start
    589 %rv_lw(a0, sp, 0)
    590 %rv_addi(a1, sp, 4)
    591 %rv_lit32_prefix(br)
    592 &p1_main
    593 %rv_jalr(ra, br, 0)
    594 %rv_addi(a7, zero, 93)
    595 %rv_ecall
    596 %endm
    597 
    598 %macro p1_syscall()
    599 # P1: a0=number, a1,a2,a3,t0,s0,s1 = args 0..5.
    600 # Linux RISC-V: a7=number, a0..a5 = args 0..5, return in a0.
    601 # SYSCALL clobbers only P1 a0; restore a1/a2/a3 after ecall.
    602 # Native a4/a5 (x14/x15) aren't P1-exposed; we use them as syscall arg
    603 # slots and don't need to save them.
    604 %rv_mov_rr(save0, a1)
    605 %rv_mov_rr(save1, a2)
    606 %rv_mov_rr(save2, a3)
    607 %rv_mov_rr(a7, a0)
    608 %rv_mov_rr(a0, save0)
    609 %rv_mov_rr(a1, save1)
    610 %rv_mov_rr(a2, save2)
    611 %rv_mov_rr(a3, t0)
    612 %rv_mov_rr(a4, s0)
    613 %rv_mov_rr(a5, s1)
    614 %rv_ecall
    615 %rv_mov_rr(a1, save0)
    616 %rv_mov_rr(a2, save1)
    617 %rv_mov_rr(a3, save2)
    618 %endm
    619 
    620 # ---- Linux RV32 syscall numbers ------------------------------------------
    621 # Each macro returns the syscall number as an integer atom so callers can
    622 # use it inside expressions (e.g. `%li(a0, %sys_write)`).
    623 
    624 %macro p1_sys_read()
    625 63
    626 %endm
    627 %macro p1_sys_write()
    628 64
    629 %endm
    630 %macro p1_sys_close()
    631 57
    632 %endm
    633 %macro p1_sys_openat()
    634 56
    635 %endm
    636 %macro p1_sys_exit()
    637 93
    638 %endm
    639 %macro p1_sys_clone()
    640 220
    641 %endm
    642 %macro p1_sys_execve()
    643 221
    644 %endm
    645 %macro p1_sys_spawn()
    646 1024
    647 %endm
    648 %macro p1_sys_waitid()
    649 95
    650 %endm
    651 %macro p1_sys_lseek()
    652 62
    653 %endm
    654 %macro p1_sys_lseek_wrapper()
    655 # Linux RV32 exposes syscall 62 as _llseek(fd, hi, lo, result, whence),
    656 # whereas the 64-bit ABIs expose lseek(fd, off, whence). Keep libp1pp's
    657 # target-word API and adapt the signed 32-bit offset here.
    658 %enter(12)
    659 %st(s0, sp, 8)
    660 %mov(s0, a2)
    661 %mov(a3, a1)
    662 %sari(a2, a1, 31)
    663 %mov(t0, sp)
    664 %mov(a1, a0)
    665 %li(a0, %p1_sys_lseek)
    666 %syscall
    667 %ld(s0, sp, 8)
    668 %bltz(a0, &@done)
    669 %ld(a0, sp, 0)
    670 :@done
    671 %eret
    672 %endm
    673 %macro p1_sys_brk()
    674 214
    675 %endm
    676 %macro p1_sys_unlinkat()
    677 35
    678 %endm