golang.org/x/arch@v0.17.0/riscv64/riscv64asm/plan9x.go (about) 1 // Copyright 2024 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package riscv64asm 6 7 import ( 8 "fmt" 9 "io" 10 "strconv" 11 "strings" 12 ) 13 14 // GoSyntax returns the Go assembler syntax for the instruction. 15 // The syntax was originally defined by Plan 9. 16 // The pc is the program counter of the instruction, used for 17 // expanding PC-relative addresses into absolute ones. 18 // The symname function queries the symbol table for the program 19 // being disassembled. Given a target address it returns the name 20 // and base address of the symbol containing the target, if any; 21 // otherwise it returns "", 0. 22 // The reader text should read from the text segment using text addresses 23 // as offsets; it is used to display pc-relative loads as constant loads. 24 func GoSyntax(inst Inst, pc uint64, symname func(uint64) (string, uint64), text io.ReaderAt) string { 25 if symname == nil { 26 symname = func(uint64) (string, uint64) { return "", 0 } 27 } 28 29 var args []string 30 for _, a := range inst.Args { 31 if a == nil { 32 break 33 } 34 args = append(args, plan9Arg(&inst, pc, symname, a)) 35 } 36 37 op := inst.Op.String() 38 39 switch inst.Op { 40 41 case AMOADD_D, AMOADD_D_AQ, AMOADD_D_RL, AMOADD_D_AQRL, AMOADD_W, AMOADD_W_AQ, 42 AMOADD_W_RL, AMOADD_W_AQRL, AMOAND_D, AMOAND_D_AQ, AMOAND_D_RL, AMOAND_D_AQRL, 43 AMOAND_W, AMOAND_W_AQ, AMOAND_W_RL, AMOAND_W_AQRL, AMOMAXU_D, AMOMAXU_D_AQ, 44 AMOMAXU_D_RL, AMOMAXU_D_AQRL, AMOMAXU_W, AMOMAXU_W_AQ, AMOMAXU_W_RL, AMOMAXU_W_AQRL, 45 AMOMAX_D, AMOMAX_D_AQ, AMOMAX_D_RL, AMOMAX_D_AQRL, AMOMAX_W, AMOMAX_W_AQ, AMOMAX_W_RL, 46 AMOMAX_W_AQRL, AMOMINU_D, AMOMINU_D_AQ, AMOMINU_D_RL, AMOMINU_D_AQRL, AMOMINU_W, 47 AMOMINU_W_AQ, AMOMINU_W_RL, AMOMINU_W_AQRL, AMOMIN_D, AMOMIN_D_AQ, AMOMIN_D_RL, 48 AMOMIN_D_AQRL, AMOMIN_W, AMOMIN_W_AQ, AMOMIN_W_RL, AMOMIN_W_AQRL, AMOOR_D, AMOOR_D_AQ, 49 AMOOR_D_RL, AMOOR_D_AQRL, AMOOR_W, AMOOR_W_AQ, AMOOR_W_RL, AMOOR_W_AQRL, AMOSWAP_D, 50 AMOSWAP_D_AQ, AMOSWAP_D_RL, AMOSWAP_D_AQRL, AMOSWAP_W, AMOSWAP_W_AQ, AMOSWAP_W_RL, 51 AMOSWAP_W_AQRL, AMOXOR_D, AMOXOR_D_AQ, AMOXOR_D_RL, AMOXOR_D_AQRL, AMOXOR_W, 52 AMOXOR_W_AQ, AMOXOR_W_RL, AMOXOR_W_AQRL, SC_D, SC_D_AQ, SC_D_RL, SC_D_AQRL, 53 SC_W, SC_W_AQ, SC_W_RL, SC_W_AQRL: 54 // Atomic instructions have special operand order. 55 args[2], args[1] = args[1], args[2] 56 57 case ADDI: 58 if inst.Args[2].(Simm).Imm == 0 { 59 op = "MOV" 60 args = args[:len(args)-1] 61 } 62 63 case ADDIW: 64 if inst.Args[2].(Simm).Imm == 0 { 65 op = "MOVW" 66 args = args[:len(args)-1] 67 } 68 69 case ANDI: 70 if inst.Args[2].(Simm).Imm == 255 { 71 op = "MOVBU" 72 args = args[:len(args)-1] 73 } 74 75 case BEQ: 76 if inst.Args[1].(Reg) == X0 { 77 op = "BEQZ" 78 args[1] = args[2] 79 args = args[:len(args)-1] 80 } 81 for i, j := 0, len(args)-1; i < j; i, j = i+1, j-1 { 82 args[i], args[j] = args[j], args[i] 83 } 84 85 case BGE: 86 if inst.Args[1].(Reg) == X0 { 87 op = "BGEZ" 88 args[1] = args[2] 89 args = args[:len(args)-1] 90 } 91 for i, j := 0, len(args)-1; i < j; i, j = i+1, j-1 { 92 args[i], args[j] = args[j], args[i] 93 } 94 95 case BLT: 96 if inst.Args[1].(Reg) == X0 { 97 op = "BLTZ" 98 args[1] = args[2] 99 args = args[:len(args)-1] 100 } 101 for i, j := 0, len(args)-1; i < j; i, j = i+1, j-1 { 102 args[i], args[j] = args[j], args[i] 103 } 104 105 case BNE: 106 if inst.Args[1].(Reg) == X0 { 107 op = "BNEZ" 108 args[1] = args[2] 109 args = args[:len(args)-1] 110 } 111 for i, j := 0, len(args)-1; i < j; i, j = i+1, j-1 { 112 args[i], args[j] = args[j], args[i] 113 } 114 115 case BLTU, BGEU: 116 for i, j := 0, len(args)-1; i < j; i, j = i+1, j-1 { 117 args[i], args[j] = args[j], args[i] 118 } 119 120 case CSRRW: 121 switch inst.Args[1].(CSR) { 122 case FCSR: 123 op = "FSCSR" 124 args[1] = args[2] 125 args = args[:len(args)-1] 126 case FFLAGS: 127 op = "FSFLAGS" 128 args[1] = args[2] 129 args = args[:len(args)-1] 130 case FRM: 131 op = "FSRM" 132 args[1] = args[2] 133 args = args[:len(args)-1] 134 case CYCLE: 135 if inst.Args[0].(Reg) == X0 && inst.Args[2].(Reg) == X0 { 136 op = "UNIMP" 137 args = nil 138 } 139 } 140 141 case CSRRS: 142 if inst.Args[2].(Reg) == X0 { 143 switch inst.Args[1].(CSR) { 144 case FCSR: 145 op = "FRCSR" 146 args = args[:len(args)-2] 147 case FFLAGS: 148 op = "FRFLAGS" 149 args = args[:len(args)-2] 150 case FRM: 151 op = "FRRM" 152 args = args[:len(args)-2] 153 case CYCLE: 154 op = "RDCYCLE" 155 args = args[:len(args)-2] 156 case CYCLEH: 157 op = "RDCYCLEH" 158 args = args[:len(args)-2] 159 case INSTRET: 160 op = "RDINSTRET" 161 args = args[:len(args)-2] 162 case INSTRETH: 163 op = "RDINSTRETH" 164 args = args[:len(args)-2] 165 case TIME: 166 op = "RDTIME" 167 args = args[:len(args)-2] 168 case TIMEH: 169 op = "RDTIMEH" 170 args = args[:len(args)-2] 171 } 172 } 173 174 // Fence instruction in plan9 doesn't have any operands. 175 case FENCE: 176 args = nil 177 178 case FMADD_D, FMADD_H, FMADD_Q, FMADD_S, FMSUB_D, FMSUB_H, 179 FMSUB_Q, FMSUB_S, FNMADD_D, FNMADD_H, FNMADD_Q, FNMADD_S, 180 FNMSUB_D, FNMSUB_H, FNMSUB_Q, FNMSUB_S: 181 args[1], args[3] = args[3], args[1] 182 183 case FSGNJ_S: 184 if inst.Args[2] == inst.Args[1] { 185 op = "MOVF" 186 args = args[:len(args)-1] 187 } 188 189 case FSGNJ_D: 190 if inst.Args[2] == inst.Args[1] { 191 op = "MOVD" 192 args = args[:len(args)-1] 193 } 194 195 case FSGNJX_S: 196 if inst.Args[2] == inst.Args[1] { 197 op = "FABSS" 198 args = args[:len(args)-1] 199 } 200 201 case FSGNJX_D: 202 if inst.Args[2] == inst.Args[1] { 203 op = "FABSD" 204 args = args[:len(args)-1] 205 } 206 207 case FSGNJN_S: 208 if inst.Args[2] == inst.Args[1] { 209 op = "FNEGS" 210 args = args[:len(args)-1] 211 } 212 213 case FSGNJN_D: 214 if inst.Args[2] == inst.Args[1] { 215 op = "FNESD" 216 args = args[:len(args)-1] 217 } 218 219 case LD, SD: 220 op = "MOV" 221 if inst.Op == SD { 222 args[0], args[1] = args[1], args[0] 223 } 224 225 case LB, SB: 226 op = "MOVB" 227 if inst.Op == SB { 228 args[0], args[1] = args[1], args[0] 229 } 230 231 case LH, SH: 232 op = "MOVH" 233 if inst.Op == SH { 234 args[0], args[1] = args[1], args[0] 235 } 236 237 case LW, SW: 238 op = "MOVW" 239 if inst.Op == SW { 240 args[0], args[1] = args[1], args[0] 241 } 242 243 case LBU: 244 op = "MOVBU" 245 246 case LHU: 247 op = "MOVHU" 248 249 case LWU: 250 op = "MOVWU" 251 252 case FLW, FSW: 253 op = "MOVF" 254 if inst.Op == FLW { 255 args[0], args[1] = args[1], args[0] 256 } 257 258 case FLD, FSD: 259 op = "MOVD" 260 if inst.Op == FLD { 261 args[0], args[1] = args[1], args[0] 262 } 263 264 case SUB: 265 if inst.Args[1].(Reg) == X0 { 266 op = "NEG" 267 args[1] = args[2] 268 args = args[:len(args)-1] 269 } 270 271 case XORI: 272 if inst.Args[2].(Simm).String() == "-1" { 273 op = "NOT" 274 args = args[:len(args)-1] 275 } 276 277 case SLTIU: 278 if inst.Args[2].(Simm).Imm == 1 { 279 op = "SEQZ" 280 args = args[:len(args)-1] 281 } 282 283 case SLTU: 284 if inst.Args[1].(Reg) == X0 { 285 op = "SNEZ" 286 args[1] = args[2] 287 args = args[:len(args)-1] 288 } 289 290 case JAL: 291 if inst.Args[0].(Reg) == X0 { 292 op = "JMP" 293 args[0] = args[1] 294 args = args[:len(args)-1] 295 } else if inst.Args[0].(Reg) == X1 { 296 op = "CALL" 297 args[0] = args[1] 298 args = args[:len(args)-1] 299 } else { 300 args[0], args[1] = args[1], args[0] 301 } 302 303 case JALR: 304 if inst.Args[0].(Reg) == X0 { 305 if inst.Args[1].(RegOffset).OfsReg == X1 && inst.Args[1].(RegOffset).Ofs.Imm == 0 { 306 op = "RET" 307 args = nil 308 break 309 } 310 op = "JMP" 311 args[0] = args[1] 312 args = args[:len(args)-1] 313 } else if inst.Args[0].(Reg) == X1 { 314 op = "CALL" 315 args[0] = args[1] 316 args = args[:len(args)-1] 317 } else { 318 args[0], args[1] = args[1], args[0] 319 } 320 } 321 322 // Reverse args, placing dest last. 323 for i, j := 0, len(args)-1; i < j; i, j = i+1, j-1 { 324 args[i], args[j] = args[j], args[i] 325 } 326 327 // Change to plan9 opcode format 328 // Atomic instructions do not have reorder suffix, so remove them 329 op = strings.Replace(op, ".AQRL", "", -1) 330 op = strings.Replace(op, ".AQ", "", -1) 331 op = strings.Replace(op, ".RL", "", -1) 332 op = strings.Replace(op, ".", "", -1) 333 334 if args != nil { 335 op += " " + strings.Join(args, ", ") 336 } 337 338 return op 339 } 340 341 func plan9Arg(inst *Inst, pc uint64, symname func(uint64) (string, uint64), arg Arg) string { 342 switch a := arg.(type) { 343 case Uimm: 344 return fmt.Sprintf("$%d", uint32(a.Imm)) 345 346 case Simm: 347 imm, _ := strconv.Atoi(a.String()) 348 if a.Width == 13 || a.Width == 21 { 349 addr := int64(pc) + int64(imm) 350 if s, base := symname(uint64(addr)); s != "" && uint64(addr) == base { 351 return fmt.Sprintf("%s(SB)", s) 352 } 353 return fmt.Sprintf("%d(PC)", imm/4) 354 } 355 return fmt.Sprintf("$%d", int32(imm)) 356 357 case Reg: 358 if a <= 31 { 359 return fmt.Sprintf("X%d", a) 360 } else { 361 return fmt.Sprintf("F%d", a-32) 362 } 363 364 case RegOffset: 365 if a.Ofs.Imm == 0 { 366 return fmt.Sprintf("(X%d)", a.OfsReg) 367 } else { 368 return fmt.Sprintf("%s(X%d)", a.Ofs.String(), a.OfsReg) 369 } 370 371 case AmoReg: 372 return fmt.Sprintf("(X%d)", a.reg) 373 374 default: 375 return strings.ToUpper(arg.String()) 376 } 377 }