boot2

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

riscv32.py (6450B)


      1 """RV32IM Linux lowering for the generated P1-32 DEFINE table."""
      2 
      3 from common import (
      4     AddI,
      5     ArchDef,
      6     BranchReg,
      7     CondB,
      8     CondBZ,
      9     Enter,
     10     La,
     11     LaBr,
     12     LdArg,
     13     Li,
     14     LogI,
     15     Mem,
     16     Mov,
     17     Nullary,
     18     Rrr,
     19     ShiftI,
     20     le32,
     21     round_up,
     22 )
     23 from riscv64 import (
     24     CONDBZ_INV_BASE,
     25     CONDB_INV_BASE,
     26     NAT,
     27     RRR_BASE,
     28     SYSCALL_NUMBERS,
     29     rv_addi,
     30     rv_b_type_skip8,
     31     rv_ecall,
     32     rv_i_type,
     33     rv_jalr,
     34     rv_lbu,
     35     rv_mov_rr,
     36     rv_r_type,
     37     rv_s_type,
     38     rv_sb,
     39 )
     40 
     41 
     42 def rv_lw(rd, ra, imm12):
     43     return rv_i_type(0x00002003, rd, ra, imm12)
     44 
     45 
     46 def rv_sw(rs, ra, imm12):
     47     return rv_s_type(0x00002023, rs, ra, imm12)
     48 
     49 
     50 def rv_slli(rd, ra, shamt):
     51     d = NAT[rd]
     52     a = NAT[ra]
     53     return le32(0x00001013 | ((shamt & 0x1F) << 20) | (a << 15) | (d << 7))
     54 
     55 
     56 def rv_srli(rd, ra, shamt):
     57     d = NAT[rd]
     58     a = NAT[ra]
     59     return le32(0x00005013 | ((shamt & 0x1F) << 20) | (a << 15) | (d << 7))
     60 
     61 
     62 def rv_srai(rd, ra, shamt):
     63     d = NAT[rd]
     64     a = NAT[ra]
     65     return le32(0x40005013 | ((shamt & 0x1F) << 20) | (a << 15) | (d << 7))
     66 
     67 
     68 def rv_lit32_prefix(rd):
     69     # auipc rd, 0 ; lw rd, 12(rd) ; jal x0, +8. The four bytes that
     70     # follow in source are both the P1 word literal and an ELF32 pointer.
     71     d = NAT[rd]
     72     auipc = 0x00000017 | (d << 7)
     73     lw = 0x00C02003 | (d << 15) | (d << 7)
     74     jal = 0x0080006F
     75     return le32(auipc) + le32(lw) + le32(jal)
     76 
     77 
     78 def rv_lit32_padded64_prefix(rd):
     79     # The stage0 .P1 sources predate P1-32 and spell immediate payloads as
     80     # eight source bytes. Load the low RV32 word, then skip the full padded
     81     # payload. The M1pp backend uses the canonical four-byte form instead.
     82     d = NAT[rd]
     83     auipc = 0x00000017 | (d << 7)
     84     lw = 0x00C02003 | (d << 15) | (d << 7)
     85     jal = 0x00C0006F
     86     return le32(auipc) + le32(lw) + le32(jal)
     87 
     88 
     89 def rv_epilogue():
     90     return rv_lw('ra', 'sp', 0) + rv_lw('fp', 'sp', 4) + rv_mov_rr('sp', 'fp')
     91 
     92 
     93 def encode_li(_arch, row):
     94     return rv_lit32_padded64_prefix(row.rd)
     95 
     96 
     97 def encode_la(_arch, row):
     98     return rv_lit32_prefix(row.rd)
     99 
    100 
    101 def encode_labr(_arch, _row):
    102     return rv_lit32_prefix('br')
    103 
    104 
    105 def encode_mov(_arch, row):
    106     # The portable stack pointer starts after the two-word hidden header.
    107     if row.rs == 'sp':
    108         return rv_addi(row.rd, 'sp', 8)
    109     return rv_mov_rr(row.rd, row.rs)
    110 
    111 
    112 def encode_rrr(_arch, row):
    113     return rv_r_type(RRR_BASE[row.op], row.rd, row.ra, row.rb)
    114 
    115 
    116 def encode_addi(_arch, row):
    117     return rv_addi(row.rd, row.ra, row.imm)
    118 
    119 
    120 def encode_logi(_arch, row):
    121     base = {'ANDI': 0x00007013, 'ORI': 0x00006013}[row.op]
    122     return rv_i_type(base, row.rd, row.ra, row.imm)
    123 
    124 
    125 def encode_shifti(_arch, row):
    126     if row.op == 'SHLI':
    127         return rv_slli(row.rd, row.ra, row.imm)
    128     if row.op == 'SHRI':
    129         return rv_srli(row.rd, row.ra, row.imm)
    130     if row.op == 'SARI':
    131         return rv_srai(row.rd, row.ra, row.imm)
    132     raise ValueError(f'unknown shift op: {row.op}')
    133 
    134 
    135 def encode_mem(_arch, row):
    136     off = row.off + 8 if row.rn == 'sp' else row.off
    137     if row.op == 'LD':
    138         return rv_lw(row.rt, row.rn, off)
    139     if row.op == 'ST':
    140         return rv_sw(row.rt, row.rn, off)
    141     if row.op == 'LB':
    142         return rv_lbu(row.rt, row.rn, off)
    143     if row.op == 'SB':
    144         return rv_sb(row.rt, row.rn, off)
    145     raise ValueError(f'unknown mem op: {row.op}')
    146 
    147 
    148 def encode_ldarg(_arch, row):
    149     # [native sp+4] holds caller sp; its portable argument area starts
    150     # eight bytes above that native pointer and advances in 4-byte words.
    151     return rv_lw('scratch', 'sp', 4) + rv_lw(row.rd, 'scratch', 8 + 4 * row.slot)
    152 
    153 
    154 def encode_branch_reg(_arch, row):
    155     if row.kind == 'BR':
    156         return rv_jalr('zero', row.rs, 0)
    157     if row.kind == 'CALLR':
    158         return rv_jalr('ra', row.rs, 0)
    159     if row.kind == 'TAILR':
    160         return rv_epilogue() + rv_jalr('zero', row.rs, 0)
    161     raise ValueError(f'unknown branch-reg kind: {row.kind}')
    162 
    163 
    164 def encode_condb(_arch, row):
    165     return rv_b_type_skip8(CONDB_INV_BASE[row.op], row.ra, row.rb) + rv_jalr('zero', 'br', 0)
    166 
    167 
    168 def encode_condbz(_arch, row):
    169     return rv_b_type_skip8(CONDBZ_INV_BASE[row.op], row.ra, 'zero') + rv_jalr('zero', 'br', 0)
    170 
    171 
    172 def encode_enter(arch, row):
    173     frame_bytes = round_up(arch.stack_align, 2 * arch.word_bytes + row.size)
    174     return (
    175         rv_addi('sp', 'sp', -frame_bytes)
    176         + rv_sw('ra', 'sp', 0)
    177         + rv_addi('fp', 'sp', frame_bytes)
    178         + rv_sw('fp', 'sp', 4)
    179     )
    180 
    181 
    182 def encode_nullary(_arch, row):
    183     if row.kind == 'B':
    184         return rv_jalr('zero', 'br', 0)
    185     if row.kind == 'CALL':
    186         return rv_jalr('ra', 'br', 0)
    187     if row.kind == 'RET':
    188         return rv_jalr('zero', 'ra', 0)
    189     if row.kind == 'ERET':
    190         return rv_epilogue() + rv_jalr('zero', 'ra', 0)
    191     if row.kind == 'TAIL':
    192         return rv_epilogue() + rv_jalr('zero', 'br', 0)
    193     if row.kind == 'SYSCALL':
    194         # Linux's RV32 and RV64 register syscall ABIs are identical.
    195         return ''.join([
    196             rv_mov_rr('save0', 'a1'),
    197             rv_mov_rr('save1', 'a2'),
    198             rv_mov_rr('save2', 'a3'),
    199             rv_mov_rr('a7', 'a0'),
    200             rv_mov_rr('a0', 'save0'),
    201             rv_mov_rr('a1', 'save1'),
    202             rv_mov_rr('a2', 'save2'),
    203             rv_mov_rr('a3', 't0'),
    204             rv_mov_rr('a4', 's0'),
    205             rv_mov_rr('a5', 's1'),
    206             rv_ecall(),
    207             rv_mov_rr('a1', 'save0'),
    208             rv_mov_rr('a2', 'save1'),
    209             rv_mov_rr('a3', 'save2'),
    210         ])
    211     raise ValueError(f'unknown nullary kind: {row.kind}')
    212 
    213 
    214 def rv_start_stub():
    215     def q(hex_bytes):
    216         return f"'{hex_bytes}'"
    217 
    218     return [
    219         ':_start',
    220         q(rv_lw('a0', 'sp', 0)),
    221         q(rv_addi('a1', 'sp', 4)),
    222         q(rv_lit32_prefix('br')),
    223         '&p1_main',
    224         q(rv_jalr('ra', 'br', 0)),
    225         q(rv_addi('a7', 'zero', 93)),
    226         q(rv_ecall()),
    227     ]
    228 
    229 
    230 ENCODERS = {
    231     Li: encode_li,
    232     La: encode_la,
    233     LaBr: encode_labr,
    234     Mov: encode_mov,
    235     Rrr: encode_rrr,
    236     AddI: encode_addi,
    237     LogI: encode_logi,
    238     ShiftI: encode_shifti,
    239     Mem: encode_mem,
    240     LdArg: encode_ldarg,
    241     Nullary: encode_nullary,
    242     BranchReg: encode_branch_reg,
    243     CondB: encode_condb,
    244     CondBZ: encode_condbz,
    245     Enter: encode_enter,
    246 }
    247 
    248 
    249 ARCH = ArchDef(
    250     name='riscv32',
    251     word_bytes=4,
    252     stack_align=16,
    253     syscall_numbers=SYSCALL_NUMBERS,
    254     encoders=ENCODERS,
    255     start_stub=rv_start_stub,
    256 )