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 )