| // Copyright 2024 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package riscv64asm |
| |
| import ( |
| "strings" |
| ) |
| |
| // GNUSyntax returns the GNU assembler syntax for the instruction, as defined by GNU binutils. |
| // This form typically matches the syntax defined in the RISC-V Instruction Set Manual. See |
| // https://github.com/riscv/riscv-isa-manual/releases/download/Ratified-IMAFDQC/riscv-spec-20191213.pdf |
| func GNUSyntax(inst Inst) string { |
| op := strings.ToLower(inst.Op.String()) |
| var args []string |
| for _, a := range inst.Args { |
| if a == nil { |
| break |
| } |
| args = append(args, strings.ToLower(a.String())) |
| } |
| |
| switch inst.Op { |
| case ADDI, ADDIW, ANDI, ORI, SLLI, SLLIW, SRAI, SRAIW, SRLI, SRLIW, XORI: |
| if inst.Op == ADDI { |
| if inst.Args[1].(Reg) == X0 && inst.Args[0].(Reg) != X0 { |
| op = "li" |
| args[1] = args[2] |
| args = args[:len(args)-1] |
| break |
| } |
| |
| if inst.Args[2].(Simm).Imm == 0 { |
| if inst.Args[0].(Reg) == X0 && inst.Args[1].(Reg) == X0 { |
| op = "nop" |
| args = nil |
| } else { |
| op = "mv" |
| args = args[:len(args)-1] |
| } |
| } |
| } |
| |
| if inst.Op == ANDI && inst.Args[2].(Simm).Imm == 255 { |
| op = "zext.b" |
| args = args[:len(args)-1] |
| } |
| |
| if inst.Op == ADDIW && inst.Args[2].(Simm).Imm == 0 { |
| op = "sext.w" |
| args = args[:len(args)-1] |
| } |
| |
| if inst.Op == XORI && inst.Args[2].(Simm).String() == "-1" { |
| op = "not" |
| args = args[:len(args)-1] |
| } |
| |
| case ADD: |
| if inst.Args[1].(Reg) == X0 { |
| op = "mv" |
| args[1] = args[2] |
| args = args[:len(args)-1] |
| } |
| |
| case BEQ: |
| if inst.Args[1].(Reg) == X0 { |
| op = "beqz" |
| args[1] = args[2] |
| args = args[:len(args)-1] |
| } |
| |
| case BGE: |
| if inst.Args[1].(Reg) == X0 { |
| op = "bgez" |
| args[1] = args[2] |
| args = args[:len(args)-1] |
| } else if inst.Args[0].(Reg) == X0 { |
| op = "blez" |
| args[0], args[1] = args[1], args[2] |
| args = args[:len(args)-1] |
| } |
| |
| case BLT: |
| if inst.Args[1].(Reg) == X0 { |
| op = "bltz" |
| args[1] = args[2] |
| args = args[:len(args)-1] |
| } else if inst.Args[0].(Reg) == X0 { |
| op = "bgtz" |
| args[0], args[1] = args[1], args[2] |
| args = args[:len(args)-1] |
| } |
| |
| case BNE: |
| if inst.Args[1].(Reg) == X0 { |
| op = "bnez" |
| args[1] = args[2] |
| args = args[:len(args)-1] |
| } |
| |
| case CSRRC: |
| if inst.Args[0].(Reg) == X0 { |
| op = "csrc" |
| args[0], args[1] = args[1], args[2] |
| args = args[:len(args)-1] |
| } |
| |
| case CSRRCI: |
| if inst.Args[0].(Reg) == X0 { |
| op = "csrci" |
| args[0], args[1] = args[1], args[2] |
| args = args[:len(args)-1] |
| } |
| |
| case CSRRS: |
| if inst.Args[2].(Reg) == X0 { |
| switch inst.Args[1].(CSR) { |
| case FCSR: |
| op = "frcsr" |
| args = args[:len(args)-2] |
| |
| case FFLAGS: |
| op = "frflags" |
| args = args[:len(args)-2] |
| |
| case FRM: |
| op = "frrm" |
| args = args[:len(args)-2] |
| |
| // rdcycleh, rdinstreth and rdtimeh are RV-32 only instructions. |
| // So not included there. |
| case CYCLE: |
| op = "rdcycle" |
| args = args[:len(args)-2] |
| |
| case INSTRET: |
| op = "rdinstret" |
| args = args[:len(args)-2] |
| |
| case TIME: |
| op = "rdtime" |
| args = args[:len(args)-2] |
| |
| default: |
| op = "csrr" |
| args = args[:len(args)-1] |
| } |
| } else if inst.Args[0].(Reg) == X0 { |
| op = "csrs" |
| args[0], args[1] = args[1], args[2] |
| args = args[:len(args)-1] |
| } |
| |
| case CSRRSI: |
| if inst.Args[0].(Reg) == X0 { |
| op = "csrsi" |
| args[0], args[1] = args[1], args[2] |
| args = args[:len(args)-1] |
| } |
| |
| case CSRRW: |
| switch inst.Args[1].(CSR) { |
| case FCSR: |
| op = "fscsr" |
| if inst.Args[0].(Reg) == X0 { |
| args[0] = args[2] |
| args = args[:len(args)-2] |
| } else { |
| args[1] = args[2] |
| args = args[:len(args)-1] |
| } |
| |
| case FFLAGS: |
| op = "fsflags" |
| if inst.Args[0].(Reg) == X0 { |
| args[0] = args[2] |
| args = args[:len(args)-2] |
| } else { |
| args[1] = args[2] |
| args = args[:len(args)-1] |
| } |
| |
| case FRM: |
| op = "fsrm" |
| if inst.Args[0].(Reg) == X0 { |
| args[0] = args[2] |
| args = args[:len(args)-2] |
| } else { |
| args[1] = args[2] |
| args = args[:len(args)-1] |
| } |
| |
| case CYCLE: |
| if inst.Args[0].(Reg) == X0 && inst.Args[2].(Reg) == X0 { |
| op = "unimp" |
| args = nil |
| } |
| |
| default: |
| if inst.Args[0].(Reg) == X0 { |
| op = "csrw" |
| args[0], args[1] = args[1], args[2] |
| args = args[:len(args)-1] |
| } |
| } |
| |
| case CSRRWI: |
| if inst.Args[0].(Reg) == X0 { |
| op = "csrwi" |
| args[0], args[1] = args[1], args[2] |
| args = args[:len(args)-1] |
| } |
| |
| // When both pred and succ equals to iorw, the GNU objdump will omit them. |
| case FENCE: |
| if inst.Args[0].(MemOrder).String() == "iorw" && |
| inst.Args[1].(MemOrder).String() == "iorw" { |
| args = nil |
| } |
| |
| case FSGNJX_D: |
| if inst.Args[1].(Reg) == inst.Args[2].(Reg) { |
| op = "fabs.d" |
| args = args[:len(args)-1] |
| } |
| |
| case FSGNJX_S: |
| if inst.Args[1].(Reg) == inst.Args[2].(Reg) { |
| op = "fabs.s" |
| args = args[:len(args)-1] |
| } |
| |
| case FSGNJ_D: |
| if inst.Args[1].(Reg) == inst.Args[2].(Reg) { |
| op = "fmv.d" |
| args = args[:len(args)-1] |
| } |
| |
| case FSGNJ_S: |
| if inst.Args[1].(Reg) == inst.Args[2].(Reg) { |
| op = "fmv.s" |
| args = args[:len(args)-1] |
| } |
| |
| case FSGNJN_D: |
| if inst.Args[1].(Reg) == inst.Args[2].(Reg) { |
| op = "fneg.d" |
| args = args[:len(args)-1] |
| } |
| |
| case FSGNJN_S: |
| if inst.Args[1].(Reg) == inst.Args[2].(Reg) { |
| op = "fneg.s" |
| args = args[:len(args)-1] |
| } |
| |
| case JAL: |
| if inst.Args[0].(Reg) == X0 { |
| op = "j" |
| args[0] = args[1] |
| args = args[:len(args)-1] |
| } else if inst.Args[0].(Reg) == X1 { |
| op = "jal" |
| args[0] = args[1] |
| args = args[:len(args)-1] |
| } |
| |
| case JALR: |
| if inst.Args[0].(Reg) == X1 && inst.Args[1].(RegOffset).Ofs.Imm == 0 { |
| args[0] = inst.Args[1].(RegOffset).OfsReg.String() |
| args = args[:len(args)-1] |
| } |
| |
| if inst.Args[0].(Reg) == X0 { |
| if inst.Args[1].(RegOffset).OfsReg == X1 && inst.Args[1].(RegOffset).Ofs.Imm == 0 { |
| op = "ret" |
| args = nil |
| } else if inst.Args[1].(RegOffset).Ofs.Imm == 0 { |
| op = "jr" |
| args[0] = inst.Args[1].(RegOffset).OfsReg.String() |
| args = args[:len(args)-1] |
| } else { |
| op = "jr" |
| args[0] = inst.Args[1].(RegOffset).String() |
| args = args[:len(args)-1] |
| } |
| } |
| |
| case SLTIU: |
| if inst.Args[2].(Simm).String() == "1" { |
| op = "seqz" |
| args = args[:len(args)-1] |
| } |
| |
| case SLT: |
| if inst.Args[1].(Reg) == X0 { |
| op = "sgtz" |
| args[1] = args[2] |
| args = args[:len(args)-1] |
| } else if inst.Args[2].(Reg) == X0 { |
| op = "sltz" |
| args = args[:len(args)-1] |
| } |
| |
| case SLTU: |
| if inst.Args[1].(Reg) == X0 { |
| op = "snez" |
| args[1] = args[2] |
| args = args[:len(args)-1] |
| } |
| |
| case SUB: |
| if inst.Args[1].(Reg) == X0 { |
| op = "neg" |
| args[1] = args[2] |
| args = args[:len(args)-1] |
| } |
| |
| case SUBW: |
| if inst.Args[1].(Reg) == X0 { |
| op = "negw" |
| args[1] = args[2] |
| args = args[:len(args)-1] |
| } |
| } |
| |
| if args != nil { |
| op += " " + strings.Join(args, ",") |
| } |
| return op |
| } |