mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
Make use of compressed instructions on riscv64 - add a compress pass to the end of the assembler, which replaces non-compressed instructions with compressed alternatives if possible. Provide a `compressinstructions` compiler and assembler debug flag, such that the compression pass can be disabled via `-asmflags=all=-d=compressinstructions=0` and `-gcflags=all=-d=compressinstructions=0`. Note that this does not prevent the explicit use of compressed instructions via assembly. Note that this does not make use of compressed control transfer instructions - this will be implemented in later changes. Reduces the text size of a hello world binary by ~121KB and reduces the text size of the go binary on riscv64 by ~1.21MB (between 8-10% in both cases). Updates #71105 Cq-Include-Trybots: luci.golang.try:gotip-linux-riscv64 Change-Id: I24258353688554042c2a836deed4830cc673e985 Reviewed-on: https://go-review.googlesource.com/c/go/+/523478 Reviewed-by: Mark Ryan <markdryan@rivosinc.com> Reviewed-by: Mark Freeman <markfreeman@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Cherry Mui <cherryyz@google.com>
5017 lines
165 KiB
Go
5017 lines
165 KiB
Go
// Copyright © 2015 The Go Authors. All rights reserved.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
|
|
package riscv
|
|
|
|
import (
|
|
"cmd/internal/obj"
|
|
"cmd/internal/objabi"
|
|
"cmd/internal/src"
|
|
"cmd/internal/sys"
|
|
"fmt"
|
|
"internal/abi"
|
|
"internal/buildcfg"
|
|
"log"
|
|
"math"
|
|
"math/bits"
|
|
"strings"
|
|
)
|
|
|
|
func buildop(ctxt *obj.Link) {}
|
|
|
|
func jalToSym(ctxt *obj.Link, p *obj.Prog, lr int16) {
|
|
switch p.As {
|
|
case obj.ACALL, obj.AJMP, obj.ARET:
|
|
default:
|
|
ctxt.Diag("unexpected Prog in jalToSym: %v", p)
|
|
return
|
|
}
|
|
|
|
p.As = AJAL
|
|
p.Mark |= NEED_JAL_RELOC
|
|
p.From.Type = obj.TYPE_REG
|
|
p.From.Reg = lr
|
|
p.Reg = obj.REG_NONE
|
|
}
|
|
|
|
// progedit is called individually for each *obj.Prog. It normalizes instruction
|
|
// formats and eliminates as many pseudo-instructions as possible.
|
|
func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
|
|
insData, err := instructionDataForAs(p.As)
|
|
if err != nil {
|
|
panic(fmt.Sprintf("failed to lookup instruction data for %v: %v", p.As, err))
|
|
}
|
|
|
|
// Expand binary instructions to ternary ones.
|
|
if p.Reg == obj.REG_NONE {
|
|
if insData.ternary {
|
|
p.Reg = p.To.Reg
|
|
}
|
|
}
|
|
|
|
// Rewrite instructions with constant operands to refer to the immediate
|
|
// form of the instruction.
|
|
if p.From.Type == obj.TYPE_CONST {
|
|
switch p.As {
|
|
case ACSUB:
|
|
p.As, p.From.Offset = ACADDI, -p.From.Offset
|
|
case ACSUBW:
|
|
p.As, p.From.Offset = ACADDIW, -p.From.Offset
|
|
case ASUB:
|
|
p.As, p.From.Offset = AADDI, -p.From.Offset
|
|
case ASUBW:
|
|
p.As, p.From.Offset = AADDIW, -p.From.Offset
|
|
default:
|
|
if insData.immForm != obj.AXXX {
|
|
p.As = insData.immForm
|
|
}
|
|
}
|
|
}
|
|
|
|
switch p.As {
|
|
case obj.AJMP:
|
|
// Turn JMP into JAL ZERO or JALR ZERO.
|
|
p.From.Type = obj.TYPE_REG
|
|
p.From.Reg = REG_ZERO
|
|
|
|
switch p.To.Type {
|
|
case obj.TYPE_BRANCH:
|
|
p.As = AJAL
|
|
case obj.TYPE_MEM:
|
|
switch p.To.Name {
|
|
case obj.NAME_NONE:
|
|
p.As = AJALR
|
|
case obj.NAME_EXTERN, obj.NAME_STATIC:
|
|
// Handled in preprocess.
|
|
default:
|
|
ctxt.Diag("unsupported name %d for %v", p.To.Name, p)
|
|
}
|
|
default:
|
|
panic(fmt.Sprintf("unhandled type %+v", p.To.Type))
|
|
}
|
|
|
|
case obj.ACALL:
|
|
switch p.To.Type {
|
|
case obj.TYPE_MEM:
|
|
// Handled in preprocess.
|
|
case obj.TYPE_REG:
|
|
p.As = AJALR
|
|
p.From.Type = obj.TYPE_REG
|
|
p.From.Reg = REG_LR
|
|
default:
|
|
ctxt.Diag("unknown destination type %+v in CALL: %v", p.To.Type, p)
|
|
}
|
|
|
|
case obj.AUNDEF:
|
|
p.As = AEBREAK
|
|
|
|
case AFMVXS:
|
|
// FMVXS is the old name for FMVXW.
|
|
p.As = AFMVXW
|
|
|
|
case AFMVSX:
|
|
// FMVSX is the old name for FMVWX.
|
|
p.As = AFMVWX
|
|
|
|
case ASCALL:
|
|
// SCALL is the old name for ECALL.
|
|
p.As = AECALL
|
|
|
|
case ASBREAK:
|
|
// SBREAK is the old name for EBREAK.
|
|
p.As = AEBREAK
|
|
|
|
case AMOV:
|
|
if p.From.Type == obj.TYPE_CONST && p.From.Name == obj.NAME_NONE && p.From.Reg == obj.REG_NONE && int64(int32(p.From.Offset)) != p.From.Offset {
|
|
if isShiftConst(p.From.Offset) {
|
|
break
|
|
}
|
|
// Put >32-bit constants in memory and load them.
|
|
p.From.Type = obj.TYPE_MEM
|
|
p.From.Sym = ctxt.Int64Sym(p.From.Offset)
|
|
p.From.Name = obj.NAME_EXTERN
|
|
p.From.Offset = 0
|
|
}
|
|
|
|
case AMOVF:
|
|
if p.From.Type == obj.TYPE_FCONST && p.From.Name == obj.NAME_NONE && p.From.Reg == obj.REG_NONE {
|
|
f64 := p.From.Val.(float64)
|
|
f32 := float32(f64)
|
|
if math.Float32bits(f32) == 0 {
|
|
p.From.Type = obj.TYPE_REG
|
|
p.From.Reg = REG_ZERO
|
|
break
|
|
}
|
|
p.From.Type = obj.TYPE_MEM
|
|
p.From.Sym = ctxt.Float32Sym(f32)
|
|
p.From.Name = obj.NAME_EXTERN
|
|
p.From.Offset = 0
|
|
}
|
|
|
|
case AMOVD:
|
|
if p.From.Type == obj.TYPE_FCONST && p.From.Name == obj.NAME_NONE && p.From.Reg == obj.REG_NONE {
|
|
f64 := p.From.Val.(float64)
|
|
if math.Float64bits(f64) == 0 {
|
|
p.From.Type = obj.TYPE_REG
|
|
p.From.Reg = REG_ZERO
|
|
break
|
|
}
|
|
p.From.Type = obj.TYPE_MEM
|
|
p.From.Sym = ctxt.Float64Sym(f64)
|
|
p.From.Name = obj.NAME_EXTERN
|
|
p.From.Offset = 0
|
|
}
|
|
}
|
|
|
|
if ctxt.Flag_dynlink {
|
|
rewriteToUseGot(ctxt, p, newprog)
|
|
}
|
|
}
|
|
|
|
// Rewrite p, if necessary, to access global data via the global offset table.
|
|
func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
|
|
// We only care about global data: NAME_EXTERN means a global
|
|
// symbol in the Go sense and p.Sym.Local is true for a few internally
|
|
// defined symbols.
|
|
if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
|
|
// MOV $sym, Rx becomes MOV sym@GOT, Rx
|
|
// MOV $sym+<off>, Rx becomes MOV sym@GOT, Rx; ADD <off>, Rx
|
|
if p.As != AMOV {
|
|
ctxt.Diag("don't know how to handle TYPE_ADDR in %v with -dynlink", p)
|
|
}
|
|
if p.To.Type != obj.TYPE_REG {
|
|
ctxt.Diag("don't know how to handle LD instruction to non-register in %v with -dynlink", p)
|
|
}
|
|
p.From.Type = obj.TYPE_MEM
|
|
p.From.Name = obj.NAME_GOTREF
|
|
if p.From.Offset != 0 {
|
|
q := obj.Appendp(p, newprog)
|
|
q.As = AADD
|
|
q.From.Type = obj.TYPE_CONST
|
|
q.From.Offset = p.From.Offset
|
|
q.To = p.To
|
|
p.From.Offset = 0
|
|
}
|
|
|
|
}
|
|
|
|
if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
|
|
ctxt.Diag("don't know how to handle %v with -dynlink", p)
|
|
}
|
|
|
|
var source *obj.Addr
|
|
// MOVx sym, Ry becomes MOV sym@GOT, X31; MOVx (X31), Ry
|
|
// MOVx Ry, sym becomes MOV sym@GOT, X31; MOV Ry, (X31)
|
|
// An addition may be inserted between the two MOVs if there is an offset.
|
|
if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
|
|
if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
|
|
ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
|
|
}
|
|
source = &p.From
|
|
} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
|
|
source = &p.To
|
|
} else {
|
|
return
|
|
}
|
|
if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
|
|
return
|
|
}
|
|
if source.Sym.Type == objabi.STLSBSS {
|
|
return
|
|
}
|
|
if source.Type != obj.TYPE_MEM {
|
|
ctxt.Diag("don't know how to handle %v with -dynlink", p)
|
|
}
|
|
p1 := obj.Appendp(p, newprog)
|
|
p1.As = AMOV
|
|
p1.From.Type = obj.TYPE_MEM
|
|
p1.From.Sym = source.Sym
|
|
p1.From.Name = obj.NAME_GOTREF
|
|
p1.To.Type = obj.TYPE_REG
|
|
p1.To.Reg = REG_TMP
|
|
|
|
p2 := obj.Appendp(p1, newprog)
|
|
p2.As = p.As
|
|
p2.From = p.From
|
|
p2.To = p.To
|
|
if p.From.Name == obj.NAME_EXTERN {
|
|
p2.From.Reg = REG_TMP
|
|
p2.From.Name = obj.NAME_NONE
|
|
p2.From.Sym = nil
|
|
} else if p.To.Name == obj.NAME_EXTERN {
|
|
p2.To.Reg = REG_TMP
|
|
p2.To.Name = obj.NAME_NONE
|
|
p2.To.Sym = nil
|
|
} else {
|
|
return
|
|
}
|
|
obj.Nopout(p)
|
|
|
|
}
|
|
|
|
// addrToReg extracts the register from an Addr, handling special Addr.Names.
|
|
func addrToReg(a obj.Addr) int16 {
|
|
switch a.Name {
|
|
case obj.NAME_PARAM, obj.NAME_AUTO:
|
|
return REG_SP
|
|
}
|
|
return a.Reg
|
|
}
|
|
|
|
// movToLoad converts a MOV mnemonic into the corresponding load instruction.
|
|
func movToLoad(mnemonic obj.As) obj.As {
|
|
switch mnemonic {
|
|
case AMOV:
|
|
return ALD
|
|
case AMOVB:
|
|
return ALB
|
|
case AMOVH:
|
|
return ALH
|
|
case AMOVW:
|
|
return ALW
|
|
case AMOVBU:
|
|
return ALBU
|
|
case AMOVHU:
|
|
return ALHU
|
|
case AMOVWU:
|
|
return ALWU
|
|
case AMOVF:
|
|
return AFLW
|
|
case AMOVD:
|
|
return AFLD
|
|
default:
|
|
panic(fmt.Sprintf("%+v is not a MOV", mnemonic))
|
|
}
|
|
}
|
|
|
|
// movToStore converts a MOV mnemonic into the corresponding store instruction.
|
|
func movToStore(mnemonic obj.As) obj.As {
|
|
switch mnemonic {
|
|
case AMOV:
|
|
return ASD
|
|
case AMOVB:
|
|
return ASB
|
|
case AMOVH:
|
|
return ASH
|
|
case AMOVW:
|
|
return ASW
|
|
case AMOVF:
|
|
return AFSW
|
|
case AMOVD:
|
|
return AFSD
|
|
default:
|
|
panic(fmt.Sprintf("%+v is not a MOV", mnemonic))
|
|
}
|
|
}
|
|
|
|
// markRelocs marks an obj.Prog that specifies a MOV pseudo-instruction and
|
|
// requires relocation.
|
|
func markRelocs(p *obj.Prog) {
|
|
switch p.As {
|
|
case AMOV, AMOVB, AMOVH, AMOVW, AMOVBU, AMOVHU, AMOVWU, AMOVF, AMOVD:
|
|
switch {
|
|
case p.From.Type == obj.TYPE_ADDR && p.To.Type == obj.TYPE_REG:
|
|
switch p.From.Name {
|
|
case obj.NAME_EXTERN, obj.NAME_STATIC:
|
|
p.Mark |= NEED_PCREL_ITYPE_RELOC
|
|
case obj.NAME_GOTREF:
|
|
p.Mark |= NEED_GOT_PCREL_ITYPE_RELOC
|
|
}
|
|
case p.From.Type == obj.TYPE_MEM && p.To.Type == obj.TYPE_REG:
|
|
switch p.From.Name {
|
|
case obj.NAME_EXTERN, obj.NAME_STATIC:
|
|
p.Mark |= NEED_PCREL_ITYPE_RELOC
|
|
case obj.NAME_GOTREF:
|
|
p.Mark |= NEED_GOT_PCREL_ITYPE_RELOC
|
|
}
|
|
case p.From.Type == obj.TYPE_REG && p.To.Type == obj.TYPE_MEM:
|
|
switch p.To.Name {
|
|
case obj.NAME_EXTERN, obj.NAME_STATIC:
|
|
p.Mark |= NEED_PCREL_STYPE_RELOC
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// InvertBranch inverts the condition of a conditional branch.
|
|
func InvertBranch(as obj.As) obj.As {
|
|
switch as {
|
|
case ABEQ:
|
|
return ABNE
|
|
case ABEQZ:
|
|
return ABNEZ
|
|
case ABGE:
|
|
return ABLT
|
|
case ABGEU:
|
|
return ABLTU
|
|
case ABGEZ:
|
|
return ABLTZ
|
|
case ABGT:
|
|
return ABLE
|
|
case ABGTU:
|
|
return ABLEU
|
|
case ABGTZ:
|
|
return ABLEZ
|
|
case ABLE:
|
|
return ABGT
|
|
case ABLEU:
|
|
return ABGTU
|
|
case ABLEZ:
|
|
return ABGTZ
|
|
case ABLT:
|
|
return ABGE
|
|
case ABLTU:
|
|
return ABGEU
|
|
case ABLTZ:
|
|
return ABGEZ
|
|
case ABNE:
|
|
return ABEQ
|
|
case ABNEZ:
|
|
return ABEQZ
|
|
case ACBEQZ:
|
|
return ACBNEZ
|
|
case ACBNEZ:
|
|
return ACBEQZ
|
|
default:
|
|
panic("InvertBranch: not a branch")
|
|
}
|
|
}
|
|
|
|
// containsCall reports whether the symbol contains a CALL (or equivalent)
|
|
// instruction. Must be called after progedit.
|
|
func containsCall(sym *obj.LSym) bool {
|
|
// CALLs are CALL or JAL(R) with link register LR.
|
|
for p := sym.Func().Text; p != nil; p = p.Link {
|
|
switch p.As {
|
|
case obj.ACALL:
|
|
return true
|
|
case ACJALR, AJAL, AJALR:
|
|
if p.From.Type == obj.TYPE_REG && p.From.Reg == REG_LR {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// setPCs sets the Pc field in all instructions reachable from p.
|
|
// It uses pc as the initial value and returns the next available pc.
|
|
func setPCs(p *obj.Prog, pc int64, compress bool) int64 {
|
|
for ; p != nil; p = p.Link {
|
|
p.Pc = pc
|
|
for _, ins := range instructionsForProg(p, compress) {
|
|
pc += int64(ins.length())
|
|
}
|
|
|
|
if p.As == obj.APCALIGN {
|
|
alignedValue := p.From.Offset
|
|
v := pcAlignPadLength(pc, alignedValue)
|
|
pc += int64(v)
|
|
}
|
|
}
|
|
return pc
|
|
}
|
|
|
|
// stackOffset updates Addr offsets based on the current stack size.
|
|
//
|
|
// The stack looks like:
|
|
// -------------------
|
|
// | |
|
|
// | PARAMs |
|
|
// | |
|
|
// | |
|
|
// -------------------
|
|
// | Parent RA | SP on function entry
|
|
// -------------------
|
|
// | |
|
|
// | |
|
|
// | AUTOs |
|
|
// | |
|
|
// | |
|
|
// -------------------
|
|
// | RA | SP during function execution
|
|
// -------------------
|
|
//
|
|
// FixedFrameSize makes other packages aware of the space allocated for RA.
|
|
//
|
|
// A nicer version of this diagram can be found on slide 21 of the presentation
|
|
// attached to https://golang.org/issue/16922#issuecomment-243748180.
|
|
func stackOffset(a *obj.Addr, stacksize int64) {
|
|
switch a.Name {
|
|
case obj.NAME_AUTO:
|
|
// Adjust to the top of AUTOs.
|
|
a.Offset += stacksize
|
|
case obj.NAME_PARAM:
|
|
// Adjust to the bottom of PARAMs.
|
|
a.Offset += stacksize + 8
|
|
}
|
|
}
|
|
|
|
// preprocess generates prologue and epilogue code, computes PC-relative branch
|
|
// and jump offsets, and resolves pseudo-registers.
|
|
//
|
|
// preprocess is called once per linker symbol.
|
|
//
|
|
// When preprocess finishes, all instructions in the symbol are either
|
|
// concrete, real RISC-V instructions or directive pseudo-ops like TEXT,
|
|
// PCDATA, and FUNCDATA.
|
|
func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
|
if cursym.Func().Text == nil || cursym.Func().Text.Link == nil {
|
|
return
|
|
}
|
|
|
|
// Generate the prologue.
|
|
text := cursym.Func().Text
|
|
if text.As != obj.ATEXT {
|
|
ctxt.Diag("preprocess: found symbol that does not start with TEXT directive")
|
|
return
|
|
}
|
|
|
|
stacksize := text.To.Offset
|
|
if stacksize == -8 {
|
|
// Historical way to mark NOFRAME.
|
|
text.From.Sym.Set(obj.AttrNoFrame, true)
|
|
stacksize = 0
|
|
}
|
|
if stacksize < 0 {
|
|
ctxt.Diag("negative frame size %d - did you mean NOFRAME?", stacksize)
|
|
}
|
|
if text.From.Sym.NoFrame() {
|
|
if stacksize != 0 {
|
|
ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", stacksize)
|
|
}
|
|
}
|
|
|
|
if !containsCall(cursym) {
|
|
text.From.Sym.Set(obj.AttrLeaf, true)
|
|
if stacksize == 0 {
|
|
// A leaf function with no locals has no frame.
|
|
text.From.Sym.Set(obj.AttrNoFrame, true)
|
|
}
|
|
}
|
|
|
|
// Save LR unless there is no frame.
|
|
if !text.From.Sym.NoFrame() {
|
|
stacksize += ctxt.Arch.FixedFrameSize
|
|
}
|
|
|
|
cursym.Func().Args = text.To.Val.(int32)
|
|
cursym.Func().Locals = int32(stacksize)
|
|
|
|
prologue := text
|
|
|
|
if !cursym.Func().Text.From.Sym.NoSplit() {
|
|
prologue = stacksplit(ctxt, prologue, cursym, newprog, stacksize) // emit split check
|
|
}
|
|
|
|
q := prologue
|
|
|
|
if stacksize != 0 {
|
|
prologue = ctxt.StartUnsafePoint(prologue, newprog)
|
|
|
|
// Actually save LR.
|
|
prologue = obj.Appendp(prologue, newprog)
|
|
prologue.As = AMOV
|
|
prologue.Pos = q.Pos
|
|
prologue.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_LR}
|
|
prologue.To = obj.Addr{Type: obj.TYPE_MEM, Reg: REG_SP, Offset: -stacksize}
|
|
|
|
// Insert stack adjustment.
|
|
prologue = obj.Appendp(prologue, newprog)
|
|
prologue.As = AADDI
|
|
prologue.Pos = q.Pos
|
|
prologue.Pos = prologue.Pos.WithXlogue(src.PosPrologueEnd)
|
|
prologue.From = obj.Addr{Type: obj.TYPE_CONST, Offset: -stacksize}
|
|
prologue.Reg = REG_SP
|
|
prologue.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_SP}
|
|
prologue.Spadj = int32(stacksize)
|
|
|
|
prologue = ctxt.EndUnsafePoint(prologue, newprog, -1)
|
|
|
|
// On Linux, in a cgo binary we may get a SIGSETXID signal early on
|
|
// before the signal stack is set, as glibc doesn't allow us to block
|
|
// SIGSETXID. So a signal may land on the current stack and clobber
|
|
// the content below the SP. We store the LR again after the SP is
|
|
// decremented.
|
|
prologue = obj.Appendp(prologue, newprog)
|
|
prologue.As = AMOV
|
|
prologue.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_LR}
|
|
prologue.To = obj.Addr{Type: obj.TYPE_MEM, Reg: REG_SP, Offset: 0}
|
|
}
|
|
|
|
// Update stack-based offsets.
|
|
for p := cursym.Func().Text; p != nil; p = p.Link {
|
|
stackOffset(&p.From, stacksize)
|
|
stackOffset(&p.To, stacksize)
|
|
}
|
|
|
|
// Additional instruction rewriting.
|
|
for p := cursym.Func().Text; p != nil; p = p.Link {
|
|
switch p.As {
|
|
case obj.AGETCALLERPC:
|
|
if cursym.Leaf() {
|
|
// MOV LR, Rd
|
|
p.As = AMOV
|
|
p.From.Type = obj.TYPE_REG
|
|
p.From.Reg = REG_LR
|
|
} else {
|
|
// MOV (RSP), Rd
|
|
p.As = AMOV
|
|
p.From.Type = obj.TYPE_MEM
|
|
p.From.Reg = REG_SP
|
|
}
|
|
|
|
case obj.ACALL:
|
|
switch p.To.Type {
|
|
case obj.TYPE_MEM:
|
|
jalToSym(ctxt, p, REG_LR)
|
|
}
|
|
|
|
case obj.AJMP:
|
|
switch p.To.Type {
|
|
case obj.TYPE_MEM:
|
|
switch p.To.Name {
|
|
case obj.NAME_EXTERN, obj.NAME_STATIC:
|
|
jalToSym(ctxt, p, REG_ZERO)
|
|
}
|
|
}
|
|
|
|
case obj.ARET:
|
|
// Replace RET with epilogue.
|
|
retJMP := p.To.Sym
|
|
|
|
if stacksize != 0 {
|
|
// Restore LR.
|
|
p.As = AMOV
|
|
p.From = obj.Addr{Type: obj.TYPE_MEM, Reg: REG_SP, Offset: 0}
|
|
p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_LR}
|
|
p = obj.Appendp(p, newprog)
|
|
|
|
p.As = AADDI
|
|
p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: stacksize}
|
|
p.Reg = REG_SP
|
|
p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_SP}
|
|
p.Spadj = int32(-stacksize)
|
|
p = obj.Appendp(p, newprog)
|
|
}
|
|
|
|
if retJMP != nil {
|
|
p.As = obj.ARET
|
|
p.To.Sym = retJMP
|
|
jalToSym(ctxt, p, REG_ZERO)
|
|
} else {
|
|
p.As = AJALR
|
|
p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_ZERO}
|
|
p.Reg = obj.REG_NONE
|
|
p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_LR}
|
|
}
|
|
|
|
// "Add back" the stack removed in the previous instruction.
|
|
//
|
|
// This is to avoid confusing pctospadj, which sums
|
|
// Spadj from function entry to each PC, and shouldn't
|
|
// count adjustments from earlier epilogues, since they
|
|
// won't affect later PCs.
|
|
p.Spadj = int32(stacksize)
|
|
|
|
case AADDI:
|
|
// Refine Spadjs account for adjustment via ADDI instruction.
|
|
if p.To.Type == obj.TYPE_REG && p.To.Reg == REG_SP && p.From.Type == obj.TYPE_CONST {
|
|
p.Spadj = int32(-p.From.Offset)
|
|
}
|
|
}
|
|
|
|
if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 {
|
|
f := cursym.Func()
|
|
if f.FuncFlag&abi.FuncFlagSPWrite == 0 {
|
|
f.FuncFlag |= abi.FuncFlagSPWrite
|
|
if ctxt.Debugvlog || !ctxt.IsAsm {
|
|
ctxt.Logf("auto-SPWRITE: %s %v\n", cursym.Name, p)
|
|
if !ctxt.IsAsm {
|
|
ctxt.Diag("invalid auto-SPWRITE in non-assembly")
|
|
ctxt.DiagFlush()
|
|
log.Fatalf("bad SPWRITE")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var callCount int
|
|
for p := cursym.Func().Text; p != nil; p = p.Link {
|
|
markRelocs(p)
|
|
if p.Mark&NEED_JAL_RELOC == NEED_JAL_RELOC {
|
|
callCount++
|
|
}
|
|
}
|
|
const callTrampSize = 8 // 2 machine instructions.
|
|
maxTrampSize := int64(callCount * callTrampSize)
|
|
|
|
// Compute instruction addresses. Once we do that, we need to check for
|
|
// overextended jumps and branches. Within each iteration, Pc differences
|
|
// are always lower bounds (since the program gets monotonically longer,
|
|
// a fixed point will be reached). No attempt to handle functions > 2GiB.
|
|
for {
|
|
big, rescan := false, false
|
|
maxPC := setPCs(cursym.Func().Text, 0, ctxt.CompressInstructions)
|
|
if maxPC+maxTrampSize > (1 << 20) {
|
|
big = true
|
|
}
|
|
|
|
for p := cursym.Func().Text; p != nil; p = p.Link {
|
|
switch p.As {
|
|
case ABEQ, ABEQZ, ABGE, ABGEU, ABGEZ, ABGT, ABGTU, ABGTZ, ABLE, ABLEU, ABLEZ, ABLT, ABLTU, ABLTZ, ABNE, ABNEZ, ACBEQZ, ACBNEZ, ACJ:
|
|
if p.To.Type != obj.TYPE_BRANCH {
|
|
ctxt.Diag("%v: instruction with branch-like opcode lacks destination", p)
|
|
break
|
|
}
|
|
offset := p.To.Target().Pc - p.Pc
|
|
if offset < -4096 || 4096 <= offset {
|
|
// Branch is long. Replace it with a jump.
|
|
jmp := obj.Appendp(p, newprog)
|
|
jmp.As = AJAL
|
|
jmp.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_ZERO}
|
|
jmp.To = obj.Addr{Type: obj.TYPE_BRANCH}
|
|
jmp.To.SetTarget(p.To.Target())
|
|
|
|
p.As = InvertBranch(p.As)
|
|
p.To.SetTarget(jmp.Link)
|
|
|
|
// We may have made previous branches too long,
|
|
// so recheck them.
|
|
rescan = true
|
|
}
|
|
case AJAL:
|
|
// Linker will handle the intersymbol case and trampolines.
|
|
if p.To.Target() == nil {
|
|
if !big {
|
|
break
|
|
}
|
|
// This function is going to be too large for JALs
|
|
// to reach trampolines. Replace with AUIPC+JALR.
|
|
jmp := obj.Appendp(p, newprog)
|
|
jmp.As = AJALR
|
|
jmp.From = p.From
|
|
jmp.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
|
|
|
|
p.As = AAUIPC
|
|
p.Mark = (p.Mark &^ NEED_JAL_RELOC) | NEED_CALL_RELOC
|
|
p.AddRestSource(obj.Addr{Type: obj.TYPE_CONST, Offset: p.To.Offset, Sym: p.To.Sym})
|
|
p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: 0}
|
|
p.Reg = obj.REG_NONE
|
|
p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
|
|
|
|
rescan = true
|
|
break
|
|
}
|
|
offset := p.To.Target().Pc - p.Pc
|
|
if offset < -(1<<20) || (1<<20) <= offset {
|
|
// Replace with 2-instruction sequence. This assumes
|
|
// that TMP is not live across J instructions, since
|
|
// it is reserved by SSA.
|
|
jmp := obj.Appendp(p, newprog)
|
|
jmp.As = AJALR
|
|
jmp.From = p.From
|
|
jmp.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
|
|
|
|
// p.From is not generally valid, however will be
|
|
// fixed up in the next loop.
|
|
p.As = AAUIPC
|
|
p.From = obj.Addr{Type: obj.TYPE_BRANCH, Sym: p.From.Sym}
|
|
p.From.SetTarget(p.To.Target())
|
|
p.Reg = obj.REG_NONE
|
|
p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_TMP}
|
|
|
|
rescan = true
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return if errors have been detected up to this point. Continuing
|
|
// may lead to duplicate errors being output.
|
|
if ctxt.Errors > 0 {
|
|
return
|
|
}
|
|
if !rescan {
|
|
break
|
|
}
|
|
}
|
|
|
|
// Now that there are no long branches, resolve branch and jump targets.
|
|
// At this point, instruction rewriting which changes the number of
|
|
// instructions will break everything--don't do it!
|
|
for p := cursym.Func().Text; p != nil; p = p.Link {
|
|
switch p.As {
|
|
case ABEQ, ABEQZ, ABGE, ABGEU, ABGEZ, ABGT, ABGTU, ABGTZ, ABLE, ABLEU, ABLEZ, ABLT, ABLTU, ABLTZ, ABNE, ABNEZ, ACBEQZ, ACBNEZ, ACJ:
|
|
switch p.To.Type {
|
|
case obj.TYPE_BRANCH:
|
|
p.To.Type, p.To.Offset = obj.TYPE_CONST, p.To.Target().Pc-p.Pc
|
|
case obj.TYPE_MEM:
|
|
if ctxt.Errors == 0 {
|
|
// An error should have already been reported for this instruction
|
|
panic("unhandled type")
|
|
}
|
|
}
|
|
|
|
case AJAL:
|
|
// Linker will handle the intersymbol case and trampolines.
|
|
if p.To.Target() != nil {
|
|
p.To.Type, p.To.Offset = obj.TYPE_CONST, p.To.Target().Pc-p.Pc
|
|
}
|
|
|
|
case AAUIPC:
|
|
if p.From.Type == obj.TYPE_BRANCH {
|
|
low, high, err := Split32BitImmediate(p.From.Target().Pc - p.Pc)
|
|
if err != nil {
|
|
ctxt.Diag("%v: jump displacement %d too large", p, p.To.Target().Pc-p.Pc)
|
|
}
|
|
p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: high, Sym: cursym}
|
|
p.Link.To.Offset = low
|
|
}
|
|
|
|
case obj.APCALIGN:
|
|
alignedValue := p.From.Offset
|
|
if (alignedValue&(alignedValue-1) != 0) || 4 > alignedValue || alignedValue > 2048 {
|
|
ctxt.Diag("alignment value of an instruction must be a power of two and in the range [4, 2048], got %d\n", alignedValue)
|
|
}
|
|
// Update the current text symbol alignment value.
|
|
if int32(alignedValue) > cursym.Func().Align {
|
|
cursym.Func().Align = int32(alignedValue)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Validate all instructions - this provides nice error messages.
|
|
for p := cursym.Func().Text; p != nil; p = p.Link {
|
|
for _, ins := range instructionsForProg(p, ctxt.CompressInstructions) {
|
|
ins.validate(ctxt)
|
|
}
|
|
}
|
|
}
|
|
|
|
func pcAlignPadLength(pc int64, alignedValue int64) int {
|
|
return int(-pc & (alignedValue - 1))
|
|
}
|
|
|
|
func stacksplit(ctxt *obj.Link, p *obj.Prog, cursym *obj.LSym, newprog obj.ProgAlloc, framesize int64) *obj.Prog {
|
|
// Leaf function with no frame is effectively NOSPLIT.
|
|
if framesize == 0 {
|
|
return p
|
|
}
|
|
|
|
if ctxt.Flag_maymorestack != "" {
|
|
// Save LR and REGCTXT
|
|
const frameSize = 16
|
|
p = ctxt.StartUnsafePoint(p, newprog)
|
|
|
|
// Spill Arguments. This has to happen before we open
|
|
// any more frame space.
|
|
p = cursym.Func().SpillRegisterArgs(p, newprog)
|
|
|
|
// MOV LR, -16(SP)
|
|
p = obj.Appendp(p, newprog)
|
|
p.As = AMOV
|
|
p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_LR}
|
|
p.To = obj.Addr{Type: obj.TYPE_MEM, Reg: REG_SP, Offset: -frameSize}
|
|
// ADDI $-16, SP
|
|
p = obj.Appendp(p, newprog)
|
|
p.As = AADDI
|
|
p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: -frameSize}
|
|
p.Reg = REG_SP
|
|
p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_SP}
|
|
p.Spadj = frameSize
|
|
// MOV REGCTXT, 8(SP)
|
|
p = obj.Appendp(p, newprog)
|
|
p.As = AMOV
|
|
p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_CTXT}
|
|
p.To = obj.Addr{Type: obj.TYPE_MEM, Reg: REG_SP, Offset: 8}
|
|
|
|
// CALL maymorestack
|
|
p = obj.Appendp(p, newprog)
|
|
p.As = obj.ACALL
|
|
p.To.Type = obj.TYPE_BRANCH
|
|
// See ../x86/obj6.go
|
|
p.To.Sym = ctxt.LookupABI(ctxt.Flag_maymorestack, cursym.ABI())
|
|
jalToSym(ctxt, p, REG_X5)
|
|
|
|
// Restore LR and REGCTXT
|
|
|
|
// MOV 8(SP), REGCTXT
|
|
p = obj.Appendp(p, newprog)
|
|
p.As = AMOV
|
|
p.From = obj.Addr{Type: obj.TYPE_MEM, Reg: REG_SP, Offset: 8}
|
|
p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_CTXT}
|
|
// MOV (SP), LR
|
|
p = obj.Appendp(p, newprog)
|
|
p.As = AMOV
|
|
p.From = obj.Addr{Type: obj.TYPE_MEM, Reg: REG_SP, Offset: 0}
|
|
p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_LR}
|
|
// ADDI $16, SP
|
|
p = obj.Appendp(p, newprog)
|
|
p.As = AADDI
|
|
p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: frameSize}
|
|
p.Reg = REG_SP
|
|
p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_SP}
|
|
p.Spadj = -frameSize
|
|
|
|
// Unspill arguments
|
|
p = cursym.Func().UnspillRegisterArgs(p, newprog)
|
|
p = ctxt.EndUnsafePoint(p, newprog, -1)
|
|
}
|
|
|
|
// Jump back to here after morestack returns.
|
|
startPred := p
|
|
|
|
// MOV g_stackguard(g), X6
|
|
p = obj.Appendp(p, newprog)
|
|
p.As = AMOV
|
|
p.From.Type = obj.TYPE_MEM
|
|
p.From.Reg = REGG
|
|
p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
|
|
if cursym.CFunc() {
|
|
p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
|
|
}
|
|
p.To.Type = obj.TYPE_REG
|
|
p.To.Reg = REG_X6
|
|
|
|
// Mark the stack bound check and morestack call async nonpreemptible.
|
|
// If we get preempted here, when resumed the preemption request is
|
|
// cleared, but we'll still call morestack, which will double the stack
|
|
// unnecessarily. See issue #35470.
|
|
p = ctxt.StartUnsafePoint(p, newprog)
|
|
|
|
var to_done, to_more *obj.Prog
|
|
|
|
if framesize <= abi.StackSmall {
|
|
// small stack
|
|
// // if SP > stackguard { goto done }
|
|
// BLTU stackguard, SP, done
|
|
p = obj.Appendp(p, newprog)
|
|
p.As = ABLTU
|
|
p.From.Type = obj.TYPE_REG
|
|
p.From.Reg = REG_X6
|
|
p.Reg = REG_SP
|
|
p.To.Type = obj.TYPE_BRANCH
|
|
to_done = p
|
|
} else {
|
|
// large stack: SP-framesize < stackguard-StackSmall
|
|
offset := framesize - abi.StackSmall
|
|
if framesize > abi.StackBig {
|
|
// Such a large stack we need to protect against underflow.
|
|
// The runtime guarantees SP > objabi.StackBig, but
|
|
// framesize is large enough that SP-framesize may
|
|
// underflow, causing a direct comparison with the
|
|
// stack guard to incorrectly succeed. We explicitly
|
|
// guard against underflow.
|
|
//
|
|
// MOV $(framesize-StackSmall), X7
|
|
// BLTU SP, X7, label-of-call-to-morestack
|
|
|
|
p = obj.Appendp(p, newprog)
|
|
p.As = AMOV
|
|
p.From.Type = obj.TYPE_CONST
|
|
p.From.Offset = offset
|
|
p.To.Type = obj.TYPE_REG
|
|
p.To.Reg = REG_X7
|
|
|
|
p = obj.Appendp(p, newprog)
|
|
p.As = ABLTU
|
|
p.From.Type = obj.TYPE_REG
|
|
p.From.Reg = REG_SP
|
|
p.Reg = REG_X7
|
|
p.To.Type = obj.TYPE_BRANCH
|
|
to_more = p
|
|
}
|
|
|
|
// Check against the stack guard. We've ensured this won't underflow.
|
|
// ADD $-(framesize-StackSmall), SP, X7
|
|
// // if X7 > stackguard { goto done }
|
|
// BLTU stackguard, X7, done
|
|
p = obj.Appendp(p, newprog)
|
|
p.As = AADDI
|
|
p.From.Type = obj.TYPE_CONST
|
|
p.From.Offset = -offset
|
|
p.Reg = REG_SP
|
|
p.To.Type = obj.TYPE_REG
|
|
p.To.Reg = REG_X7
|
|
|
|
p = obj.Appendp(p, newprog)
|
|
p.As = ABLTU
|
|
p.From.Type = obj.TYPE_REG
|
|
p.From.Reg = REG_X6
|
|
p.Reg = REG_X7
|
|
p.To.Type = obj.TYPE_BRANCH
|
|
to_done = p
|
|
}
|
|
|
|
// Spill the register args that could be clobbered by the
|
|
// morestack code
|
|
p = ctxt.EmitEntryStackMap(cursym, p, newprog)
|
|
p = cursym.Func().SpillRegisterArgs(p, newprog)
|
|
|
|
// CALL runtime.morestack(SB)
|
|
p = obj.Appendp(p, newprog)
|
|
p.As = obj.ACALL
|
|
p.To.Type = obj.TYPE_BRANCH
|
|
|
|
if cursym.CFunc() {
|
|
p.To.Sym = ctxt.Lookup("runtime.morestackc")
|
|
} else if !cursym.Func().Text.From.Sym.NeedCtxt() {
|
|
p.To.Sym = ctxt.Lookup("runtime.morestack_noctxt")
|
|
} else {
|
|
p.To.Sym = ctxt.Lookup("runtime.morestack")
|
|
}
|
|
if to_more != nil {
|
|
to_more.To.SetTarget(p)
|
|
}
|
|
jalToSym(ctxt, p, REG_X5)
|
|
|
|
// The instructions which unspill regs should be preemptible.
|
|
p = ctxt.EndUnsafePoint(p, newprog, -1)
|
|
p = cursym.Func().UnspillRegisterArgs(p, newprog)
|
|
|
|
// JMP start
|
|
p = obj.Appendp(p, newprog)
|
|
p.As = AJAL
|
|
p.To = obj.Addr{Type: obj.TYPE_BRANCH}
|
|
p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_ZERO}
|
|
p.To.SetTarget(startPred.Link)
|
|
|
|
// placeholder for to_done's jump target
|
|
p = obj.Appendp(p, newprog)
|
|
p.As = obj.ANOP // zero-width place holder
|
|
to_done.To.SetTarget(p)
|
|
|
|
return p
|
|
}
|
|
|
|
// signExtend sign extends val starting at bit bit.
|
|
func signExtend(val int64, bit uint) int64 {
|
|
return val << (64 - bit) >> (64 - bit)
|
|
}
|
|
|
|
// Split32BitImmediate splits a signed 32-bit immediate into a signed 20-bit
|
|
// upper immediate and a signed 12-bit lower immediate to be added to the upper
|
|
// result. For example, high may be used in LUI and low in a following ADDI to
|
|
// generate a full 32-bit constant.
|
|
func Split32BitImmediate(imm int64) (low, high int64, err error) {
|
|
if err := immIFits(imm, 32); err != nil {
|
|
return 0, 0, err
|
|
}
|
|
|
|
// Nothing special needs to be done if the immediate fits in 12 bits.
|
|
if err := immIFits(imm, 12); err == nil {
|
|
return imm, 0, nil
|
|
}
|
|
|
|
high = imm >> 12
|
|
|
|
// The bottom 12 bits will be treated as signed.
|
|
//
|
|
// If that will result in a negative 12 bit number, add 1 to
|
|
// our upper bits to adjust for the borrow.
|
|
//
|
|
// It is not possible for this increment to overflow. To
|
|
// overflow, the 20 top bits would be 1, and the sign bit for
|
|
// the low 12 bits would be set, in which case the entire 32
|
|
// bit pattern fits in a 12 bit signed value.
|
|
if imm&(1<<11) != 0 {
|
|
high++
|
|
}
|
|
|
|
low = signExtend(imm, 12)
|
|
high = signExtend(high, 20)
|
|
|
|
return low, high, nil
|
|
}
|
|
|
|
func regVal(r, min, max uint32) uint32 {
|
|
if r < min || r > max {
|
|
panic(fmt.Sprintf("register out of range, want %d <= %d <= %d", min, r, max))
|
|
}
|
|
return r - min
|
|
}
|
|
|
|
// regCI returns an integer register for use in a compressed instruction.
|
|
func regCI(r uint32) uint32 {
|
|
return regVal(r, REG_X8, REG_X15)
|
|
}
|
|
|
|
// regCF returns a float register for use in a compressed instruction.
|
|
func regCF(r uint32) uint32 {
|
|
return regVal(r, REG_F8, REG_F15)
|
|
}
|
|
|
|
// regI returns an integer register.
|
|
func regI(r uint32) uint32 {
|
|
return regVal(r, REG_X0, REG_X31)
|
|
}
|
|
|
|
// regF returns a float register.
|
|
func regF(r uint32) uint32 {
|
|
return regVal(r, REG_F0, REG_F31)
|
|
}
|
|
|
|
// regV returns a vector register.
|
|
func regV(r uint32) uint32 {
|
|
return regVal(r, REG_V0, REG_V31)
|
|
}
|
|
|
|
// immEven checks that the immediate is a multiple of two. If it
|
|
// is not, an error is returned.
|
|
func immEven(x int64) error {
|
|
if x&1 != 0 {
|
|
return fmt.Errorf("immediate %#x is not a multiple of two", x)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func immFits(x int64, nbits uint, signed bool) error {
|
|
label := "unsigned"
|
|
min, max := int64(0), int64(1)<<nbits-1
|
|
if signed {
|
|
label = "signed"
|
|
sbits := nbits - 1
|
|
min, max = int64(-1)<<sbits, int64(1)<<sbits-1
|
|
}
|
|
if x < min || x > max {
|
|
if nbits <= 16 {
|
|
return fmt.Errorf("%s immediate %d must be in range [%d, %d] (%d bits)", label, x, min, max, nbits)
|
|
}
|
|
return fmt.Errorf("%s immediate %#x must be in range [%#x, %#x] (%d bits)", label, x, min, max, nbits)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// immIFits checks whether the immediate value x fits in nbits bits
|
|
// as a signed integer. If it does not, an error is returned.
|
|
func immIFits(x int64, nbits uint) error {
|
|
return immFits(x, nbits, true)
|
|
}
|
|
|
|
// immI extracts the signed integer of the specified size from an immediate.
|
|
func immI(as obj.As, imm int64, nbits uint) uint32 {
|
|
if err := immIFits(imm, nbits); err != nil {
|
|
panic(fmt.Sprintf("%v: %v", as, err))
|
|
}
|
|
return uint32(imm) & ((1 << nbits) - 1)
|
|
}
|
|
|
|
func wantImmI(ctxt *obj.Link, ins *instruction, imm int64, nbits uint) {
|
|
if err := immIFits(imm, nbits); err != nil {
|
|
ctxt.Diag("%v: %v", ins, err)
|
|
}
|
|
}
|
|
|
|
// immUFits checks whether the immediate value x fits in nbits bits
|
|
// as an unsigned integer. If it does not, an error is returned.
|
|
func immUFits(x int64, nbits uint) error {
|
|
return immFits(x, nbits, false)
|
|
}
|
|
|
|
// immU extracts the unsigned integer of the specified size from an immediate.
|
|
func immU(as obj.As, imm int64, nbits uint) uint32 {
|
|
if err := immUFits(imm, nbits); err != nil {
|
|
panic(fmt.Sprintf("%v: %v", as, err))
|
|
}
|
|
return uint32(imm) & ((1 << nbits) - 1)
|
|
}
|
|
|
|
func wantImmU(ctxt *obj.Link, ins *instruction, imm int64, nbits uint) {
|
|
if err := immUFits(imm, nbits); err != nil {
|
|
ctxt.Diag("%v: %v", ins, err)
|
|
}
|
|
}
|
|
|
|
func isScaledImmI(imm int64, nbits uint, scale int64) bool {
|
|
return immFits(imm, nbits, true) == nil && imm%scale == 0
|
|
}
|
|
|
|
func isScaledImmU(imm int64, nbits uint, scale int64) bool {
|
|
return immFits(imm, nbits, false) == nil && imm%scale == 0
|
|
}
|
|
|
|
func wantScaledImm(ctxt *obj.Link, ins *instruction, imm int64, nbits uint, scale int64, signed bool) {
|
|
if err := immFits(imm, nbits, signed); err != nil {
|
|
ctxt.Diag("%v: %v", ins, err)
|
|
return
|
|
}
|
|
if imm%scale != 0 {
|
|
ctxt.Diag("%v: unsigned immediate %d must be a multiple of %d", ins, imm, scale)
|
|
}
|
|
}
|
|
|
|
func wantScaledImmI(ctxt *obj.Link, ins *instruction, imm int64, nbits uint, scale int64) {
|
|
wantScaledImm(ctxt, ins, imm, nbits, scale, true)
|
|
}
|
|
|
|
func wantScaledImmU(ctxt *obj.Link, ins *instruction, imm int64, nbits uint, scale int64) {
|
|
wantScaledImm(ctxt, ins, imm, nbits, scale, false)
|
|
}
|
|
|
|
func wantReg(ctxt *obj.Link, ins *instruction, pos string, descr string, r, min, max uint32) {
|
|
if r < min || r > max {
|
|
var suffix string
|
|
if r != obj.REG_NONE {
|
|
suffix = fmt.Sprintf(" but got non-%s register %s", descr, RegName(int(r)))
|
|
}
|
|
ctxt.Diag("%v: expected %s register in %s position%s", ins, descr, pos, suffix)
|
|
}
|
|
}
|
|
|
|
func wantNoneReg(ctxt *obj.Link, ins *instruction, pos string, r uint32) {
|
|
if r != obj.REG_NONE {
|
|
ctxt.Diag("%v: expected no register in %s but got register %s", ins, pos, RegName(int(r)))
|
|
}
|
|
}
|
|
|
|
// wantIntReg checks that r is an integer register.
|
|
func wantIntReg(ctxt *obj.Link, ins *instruction, pos string, r uint32) {
|
|
wantReg(ctxt, ins, pos, "integer", r, REG_X0, REG_X31)
|
|
}
|
|
|
|
func isIntPrimeReg(r uint32) bool {
|
|
return r >= REG_X8 && r <= REG_X15
|
|
}
|
|
|
|
// wantIntPrimeReg checks that r is an integer register that can be used
|
|
// in a prime register field of a compressed instruction.
|
|
func wantIntPrimeReg(ctxt *obj.Link, ins *instruction, pos string, r uint32) {
|
|
wantReg(ctxt, ins, pos, "integer prime", r, REG_X8, REG_X15)
|
|
}
|
|
|
|
// wantFloatReg checks that r is a floating-point register.
|
|
func wantFloatReg(ctxt *obj.Link, ins *instruction, pos string, r uint32) {
|
|
wantReg(ctxt, ins, pos, "float", r, REG_F0, REG_F31)
|
|
}
|
|
|
|
func isFloatPrimeReg(r uint32) bool {
|
|
return r >= REG_F8 && r <= REG_F15
|
|
}
|
|
|
|
// wantFloatPrimeReg checks that r is an floating-point register that can
|
|
// be used in a prime register field of a compressed instruction.
|
|
func wantFloatPrimeReg(ctxt *obj.Link, ins *instruction, pos string, r uint32) {
|
|
wantReg(ctxt, ins, pos, "float prime", r, REG_F8, REG_F15)
|
|
}
|
|
|
|
// wantVectorReg checks that r is a vector register.
|
|
func wantVectorReg(ctxt *obj.Link, ins *instruction, pos string, r uint32) {
|
|
wantReg(ctxt, ins, pos, "vector", r, REG_V0, REG_V31)
|
|
}
|
|
|
|
// wantEvenOffset checks that the offset is a multiple of two.
|
|
func wantEvenOffset(ctxt *obj.Link, ins *instruction, offset int64) {
|
|
if err := immEven(offset); err != nil {
|
|
ctxt.Diag("%v: %v", ins, err)
|
|
}
|
|
}
|
|
|
|
func validateCA(ctxt *obj.Link, ins *instruction) {
|
|
wantIntPrimeReg(ctxt, ins, "rd", ins.rd)
|
|
if ins.rd != ins.rs1 {
|
|
ctxt.Diag("%v: rd must be the same as rs1", ins)
|
|
}
|
|
wantIntPrimeReg(ctxt, ins, "rs2", ins.rs2)
|
|
}
|
|
|
|
func validateCB(ctxt *obj.Link, ins *instruction) {
|
|
if (ins.as == ACSRAI || ins.as == ACSRLI) && ins.imm == 0 {
|
|
ctxt.Diag("%v: immediate cannot be zero", ins)
|
|
} else if ins.as == ACSRAI || ins.as == ACSRLI {
|
|
wantImmU(ctxt, ins, ins.imm, 6)
|
|
} else if ins.as == ACBEQZ || ins.as == ACBNEZ {
|
|
wantImmI(ctxt, ins, ins.imm, 9)
|
|
} else {
|
|
wantImmI(ctxt, ins, ins.imm, 6)
|
|
}
|
|
if ins.as == ACBEQZ || ins.as == ACBNEZ {
|
|
wantNoneReg(ctxt, ins, "rd", ins.rd)
|
|
wantIntPrimeReg(ctxt, ins, "rs1", ins.rs1)
|
|
} else {
|
|
wantIntPrimeReg(ctxt, ins, "rd", ins.rd)
|
|
if ins.rd != ins.rs1 {
|
|
ctxt.Diag("%v: rd must be the same as rs1", ins)
|
|
}
|
|
}
|
|
wantNoneReg(ctxt, ins, "rs2", ins.rs2)
|
|
}
|
|
|
|
func validateCI(ctxt *obj.Link, ins *instruction) {
|
|
if ins.as != ACNOP && ins.rd == REG_X0 {
|
|
ctxt.Diag("%v: cannot use register X0 in rd", ins)
|
|
}
|
|
if ins.as == ACLUI && ins.rd == REG_X2 {
|
|
ctxt.Diag("%v: cannot use register SP/X2 in rd", ins)
|
|
}
|
|
if ins.as != ACLI && ins.as != ACLUI && ins.as != ACLWSP && ins.as != ACLDSP && ins.as != ACFLDSP && ins.rd != ins.rs1 {
|
|
ctxt.Diag("%v: rd must be the same as rs1", ins)
|
|
}
|
|
if ins.as == ACADDI16SP && ins.rd != REG_SP {
|
|
ctxt.Diag("%v: rd must be SP/X2", ins)
|
|
}
|
|
if (ins.as == ACLWSP || ins.as == ACLDSP || ins.as == ACFLDSP) && ins.rs2 != REG_SP {
|
|
ctxt.Diag("%v: rs2 must be SP/X2", ins)
|
|
}
|
|
if (ins.as == ACADDI || ins.as == ACADDI16SP || ins.as == ACLUI || ins.as == ACSLLI) && ins.imm == 0 {
|
|
ctxt.Diag("%v: immediate cannot be zero", ins)
|
|
} else if ins.as == ACSLLI {
|
|
wantImmU(ctxt, ins, ins.imm, 6)
|
|
} else if ins.as == ACLWSP {
|
|
wantScaledImmU(ctxt, ins, ins.imm, 8, 4)
|
|
} else if ins.as == ACLDSP || ins.as == ACFLDSP {
|
|
wantScaledImmU(ctxt, ins, ins.imm, 9, 8)
|
|
} else if ins.as == ACADDI16SP {
|
|
wantScaledImmI(ctxt, ins, ins.imm, 10, 16)
|
|
} else {
|
|
wantImmI(ctxt, ins, ins.imm, 6)
|
|
}
|
|
switch ins.as {
|
|
case ACNOP, ACADDI, ACADDIW, ACSLLI:
|
|
wantIntReg(ctxt, ins, "rd", ins.rd)
|
|
wantIntReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantNoneReg(ctxt, ins, "rs2", ins.rs2)
|
|
case ACLWSP, ACLDSP:
|
|
wantIntReg(ctxt, ins, "rd", ins.rd)
|
|
wantNoneReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantIntReg(ctxt, ins, "rs2", ins.rs2)
|
|
case ACFLDSP:
|
|
wantFloatReg(ctxt, ins, "rd", ins.rd)
|
|
wantNoneReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantIntReg(ctxt, ins, "rs2", ins.rs2)
|
|
case ACADDI16SP:
|
|
wantIntReg(ctxt, ins, "rd", ins.rd)
|
|
wantIntReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantNoneReg(ctxt, ins, "rs2", ins.rs2)
|
|
default:
|
|
wantIntReg(ctxt, ins, "rd", ins.rd)
|
|
wantNoneReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantNoneReg(ctxt, ins, "rs2", ins.rs2)
|
|
}
|
|
}
|
|
|
|
func validateCIW(ctxt *obj.Link, ins *instruction) {
|
|
wantScaledImmU(ctxt, ins, ins.imm, 10, 4)
|
|
wantIntPrimeReg(ctxt, ins, "rd", ins.rd)
|
|
wantIntReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantNoneReg(ctxt, ins, "rs2", ins.rs2)
|
|
if ins.imm == 0 {
|
|
ctxt.Diag("%v: immediate cannot be zero", ins)
|
|
}
|
|
if ins.rs1 != REG_SP {
|
|
ctxt.Diag("%v: SP/X2 must be in rs1", ins)
|
|
}
|
|
}
|
|
|
|
func validateCJ(ctxt *obj.Link, ins *instruction) {
|
|
wantEvenOffset(ctxt, ins, ins.imm)
|
|
wantImmI(ctxt, ins, ins.imm, 12)
|
|
if ins.as != ACJ {
|
|
wantNoneReg(ctxt, ins, "rd", ins.rd)
|
|
wantIntReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantIntReg(ctxt, ins, "rs2", ins.rs2)
|
|
if ins.rs1 == REG_X0 {
|
|
ctxt.Diag("%v: cannot use register X0 in rs1", ins)
|
|
}
|
|
}
|
|
}
|
|
|
|
func validateCL(ctxt *obj.Link, ins *instruction) {
|
|
if ins.as == ACLW {
|
|
wantScaledImmU(ctxt, ins, ins.imm, 7, 4)
|
|
} else if ins.as == ACLD || ins.as == ACFLD {
|
|
wantScaledImmU(ctxt, ins, ins.imm, 8, 8)
|
|
} else {
|
|
wantImmI(ctxt, ins, ins.imm, 5)
|
|
}
|
|
if ins.as == ACFLD {
|
|
wantFloatPrimeReg(ctxt, ins, "rd", ins.rd)
|
|
} else {
|
|
wantIntPrimeReg(ctxt, ins, "rd", ins.rd)
|
|
}
|
|
wantIntPrimeReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantNoneReg(ctxt, ins, "rs2", ins.rs2)
|
|
}
|
|
|
|
func validateCR(ctxt *obj.Link, ins *instruction) {
|
|
switch ins.as {
|
|
case ACJR, ACJALR:
|
|
wantNoneReg(ctxt, ins, "rd", ins.rd)
|
|
wantIntReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantNoneReg(ctxt, ins, "rs2", ins.rs2)
|
|
if ins.rs1 == REG_X0 {
|
|
ctxt.Diag("%v: cannot use register X0 in rs1", ins)
|
|
}
|
|
case ACMV:
|
|
wantIntReg(ctxt, ins, "rd", ins.rd)
|
|
wantNoneReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantIntReg(ctxt, ins, "rs2", ins.rs2)
|
|
if ins.rd == REG_X0 {
|
|
ctxt.Diag("%v: cannot use register X0 in rd", ins)
|
|
}
|
|
if ins.rs2 == REG_X0 {
|
|
ctxt.Diag("%v: cannot use register X0 in rs2", ins)
|
|
}
|
|
case ACEBREAK:
|
|
wantNoneReg(ctxt, ins, "rd", ins.rd)
|
|
wantNoneReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantNoneReg(ctxt, ins, "rs2", ins.rs2)
|
|
case ACADD:
|
|
wantIntReg(ctxt, ins, "rd", ins.rd)
|
|
if ins.rd == REG_X0 {
|
|
ctxt.Diag("%v: cannot use register X0 in rd", ins)
|
|
}
|
|
if ins.rd != ins.rs1 {
|
|
ctxt.Diag("%v: rd must be the same as rs1", ins)
|
|
}
|
|
wantIntReg(ctxt, ins, "rs2", ins.rs2)
|
|
if ins.rs2 == REG_X0 {
|
|
ctxt.Diag("%v: cannot use register X0 in rs2", ins)
|
|
}
|
|
}
|
|
}
|
|
|
|
func validateCS(ctxt *obj.Link, ins *instruction) {
|
|
if ins.as == ACSW {
|
|
wantScaledImmU(ctxt, ins, ins.imm, 7, 4)
|
|
} else if ins.as == ACSD || ins.as == ACFSD {
|
|
wantScaledImmU(ctxt, ins, ins.imm, 8, 8)
|
|
} else {
|
|
wantImmI(ctxt, ins, ins.imm, 5)
|
|
}
|
|
wantNoneReg(ctxt, ins, "rd", ins.rd)
|
|
wantIntPrimeReg(ctxt, ins, "rs1", ins.rs1)
|
|
if ins.as == ACFSD {
|
|
wantFloatPrimeReg(ctxt, ins, "rs2", ins.rs2)
|
|
} else {
|
|
wantIntPrimeReg(ctxt, ins, "rs2", ins.rs2)
|
|
}
|
|
}
|
|
|
|
func validateCSS(ctxt *obj.Link, ins *instruction) {
|
|
if ins.rd != REG_SP {
|
|
ctxt.Diag("%v: rd must be SP/X2", ins)
|
|
}
|
|
if ins.as == ACSWSP {
|
|
wantScaledImmU(ctxt, ins, ins.imm, 8, 4)
|
|
} else if ins.as == ACSDSP || ins.as == ACFSDSP {
|
|
wantScaledImmU(ctxt, ins, ins.imm, 9, 8)
|
|
} else {
|
|
wantImmI(ctxt, ins, ins.imm, 6)
|
|
}
|
|
wantNoneReg(ctxt, ins, "rs1", ins.rs1)
|
|
if ins.as == ACFSDSP {
|
|
wantFloatReg(ctxt, ins, "rs2", ins.rs2)
|
|
} else {
|
|
wantIntReg(ctxt, ins, "rs2", ins.rs2)
|
|
}
|
|
}
|
|
|
|
func validateRII(ctxt *obj.Link, ins *instruction) {
|
|
wantIntReg(ctxt, ins, "rd", ins.rd)
|
|
wantIntReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantNoneReg(ctxt, ins, "rs2", ins.rs2)
|
|
wantNoneReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateRIII(ctxt *obj.Link, ins *instruction) {
|
|
wantIntReg(ctxt, ins, "rd", ins.rd)
|
|
wantIntReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantIntReg(ctxt, ins, "rs2", ins.rs2)
|
|
wantNoneReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateRFFF(ctxt *obj.Link, ins *instruction) {
|
|
wantFloatReg(ctxt, ins, "rd", ins.rd)
|
|
wantFloatReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantFloatReg(ctxt, ins, "rs2", ins.rs2)
|
|
wantNoneReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateRFFFF(ctxt *obj.Link, ins *instruction) {
|
|
wantFloatReg(ctxt, ins, "rd", ins.rd)
|
|
wantFloatReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantFloatReg(ctxt, ins, "rs2", ins.rs2)
|
|
wantFloatReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateRFFI(ctxt *obj.Link, ins *instruction) {
|
|
wantIntReg(ctxt, ins, "rd", ins.rd)
|
|
wantFloatReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantFloatReg(ctxt, ins, "rs2", ins.rs2)
|
|
wantNoneReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateRFI(ctxt *obj.Link, ins *instruction) {
|
|
wantIntReg(ctxt, ins, "rd", ins.rd)
|
|
wantNoneReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantFloatReg(ctxt, ins, "rs2", ins.rs2)
|
|
wantNoneReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateRFV(ctxt *obj.Link, ins *instruction) {
|
|
wantVectorReg(ctxt, ins, "vd", ins.rd)
|
|
wantNoneReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantFloatReg(ctxt, ins, "rs2", ins.rs2)
|
|
wantNoneReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateRFF(ctxt *obj.Link, ins *instruction) {
|
|
wantFloatReg(ctxt, ins, "rd", ins.rd)
|
|
wantNoneReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantFloatReg(ctxt, ins, "rs2", ins.rs2)
|
|
wantNoneReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateRIF(ctxt *obj.Link, ins *instruction) {
|
|
wantFloatReg(ctxt, ins, "rd", ins.rd)
|
|
wantNoneReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantIntReg(ctxt, ins, "rs2", ins.rs2)
|
|
wantNoneReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateRIV(ctxt *obj.Link, ins *instruction) {
|
|
wantVectorReg(ctxt, ins, "vd", ins.rd)
|
|
wantNoneReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantIntReg(ctxt, ins, "rs2", ins.rs2)
|
|
wantNoneReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateRVF(ctxt *obj.Link, ins *instruction) {
|
|
wantFloatReg(ctxt, ins, "rd", ins.rd)
|
|
wantNoneReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantVectorReg(ctxt, ins, "vs2", ins.rs2)
|
|
wantNoneReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateRVFV(ctxt *obj.Link, ins *instruction) {
|
|
wantVectorReg(ctxt, ins, "vd", ins.rd)
|
|
wantFloatReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantVectorReg(ctxt, ins, "vs2", ins.rs2)
|
|
wantNoneReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateRVI(ctxt *obj.Link, ins *instruction) {
|
|
wantIntReg(ctxt, ins, "rd", ins.rd)
|
|
wantNoneReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantVectorReg(ctxt, ins, "vs2", ins.rs2)
|
|
wantNoneReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateRVIV(ctxt *obj.Link, ins *instruction) {
|
|
wantVectorReg(ctxt, ins, "vd", ins.rd)
|
|
wantIntReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantVectorReg(ctxt, ins, "vs2", ins.rs2)
|
|
wantNoneReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateRVV(ctxt *obj.Link, ins *instruction) {
|
|
wantVectorReg(ctxt, ins, "vd", ins.rd)
|
|
wantNoneReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantVectorReg(ctxt, ins, "vs2", ins.rs2)
|
|
wantNoneReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateRVVi(ctxt *obj.Link, ins *instruction) {
|
|
wantImmI(ctxt, ins, ins.imm, 5)
|
|
wantVectorReg(ctxt, ins, "vd", ins.rd)
|
|
wantNoneReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantVectorReg(ctxt, ins, "vs2", ins.rs2)
|
|
wantNoneReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateRVVu(ctxt *obj.Link, ins *instruction) {
|
|
wantImmU(ctxt, ins, ins.imm, 5)
|
|
wantVectorReg(ctxt, ins, "vd", ins.rd)
|
|
wantNoneReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantVectorReg(ctxt, ins, "vs2", ins.rs2)
|
|
wantNoneReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateRVVV(ctxt *obj.Link, ins *instruction) {
|
|
wantVectorReg(ctxt, ins, "vd", ins.rd)
|
|
wantVectorReg(ctxt, ins, "vs1", ins.rs1)
|
|
wantVectorReg(ctxt, ins, "vs2", ins.rs2)
|
|
wantNoneReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateIII(ctxt *obj.Link, ins *instruction) {
|
|
wantImmI(ctxt, ins, ins.imm, 12)
|
|
wantIntReg(ctxt, ins, "rd", ins.rd)
|
|
wantIntReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantNoneReg(ctxt, ins, "rs2", ins.rs2)
|
|
wantNoneReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateIF(ctxt *obj.Link, ins *instruction) {
|
|
wantImmI(ctxt, ins, ins.imm, 12)
|
|
wantFloatReg(ctxt, ins, "rd", ins.rd)
|
|
wantIntReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantNoneReg(ctxt, ins, "rs2", ins.rs2)
|
|
wantNoneReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateIV(ctxt *obj.Link, ins *instruction) {
|
|
wantVectorReg(ctxt, ins, "vd", ins.rd)
|
|
wantIntReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantNoneReg(ctxt, ins, "rs2", ins.rs2)
|
|
wantNoneReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateIIIV(ctxt *obj.Link, ins *instruction) {
|
|
wantVectorReg(ctxt, ins, "vd", ins.rd)
|
|
wantIntReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantIntReg(ctxt, ins, "rs2", ins.rs2)
|
|
wantNoneReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateIVIV(ctxt *obj.Link, ins *instruction) {
|
|
wantVectorReg(ctxt, ins, "vd", ins.rd)
|
|
wantIntReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantVectorReg(ctxt, ins, "vs2", ins.rs2)
|
|
wantNoneReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateSI(ctxt *obj.Link, ins *instruction) {
|
|
wantImmI(ctxt, ins, ins.imm, 12)
|
|
wantIntReg(ctxt, ins, "rd", ins.rd)
|
|
wantIntReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantNoneReg(ctxt, ins, "rs2", ins.rs2)
|
|
wantNoneReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateSF(ctxt *obj.Link, ins *instruction) {
|
|
wantImmI(ctxt, ins, ins.imm, 12)
|
|
wantIntReg(ctxt, ins, "rd", ins.rd)
|
|
wantFloatReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantNoneReg(ctxt, ins, "rs2", ins.rs2)
|
|
wantNoneReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateSV(ctxt *obj.Link, ins *instruction) {
|
|
wantIntReg(ctxt, ins, "rd", ins.rd)
|
|
wantVectorReg(ctxt, ins, "vs1", ins.rs1)
|
|
wantNoneReg(ctxt, ins, "rs2", ins.rs2)
|
|
wantNoneReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateSVII(ctxt *obj.Link, ins *instruction) {
|
|
wantVectorReg(ctxt, ins, "vd", ins.rd)
|
|
wantIntReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantIntReg(ctxt, ins, "rs2", ins.rs2)
|
|
wantNoneReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateSVIV(ctxt *obj.Link, ins *instruction) {
|
|
wantVectorReg(ctxt, ins, "vd", ins.rd)
|
|
wantIntReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantVectorReg(ctxt, ins, "vs2", ins.rs2)
|
|
wantNoneReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateB(ctxt *obj.Link, ins *instruction) {
|
|
// Offsets are multiples of two, so accept 13 bit immediates for the
|
|
// 12 bit slot. We implicitly drop the least significant bit in encodeB.
|
|
wantEvenOffset(ctxt, ins, ins.imm)
|
|
wantImmI(ctxt, ins, ins.imm, 13)
|
|
wantNoneReg(ctxt, ins, "rd", ins.rd)
|
|
wantIntReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantIntReg(ctxt, ins, "rs2", ins.rs2)
|
|
wantNoneReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateU(ctxt *obj.Link, ins *instruction) {
|
|
wantImmI(ctxt, ins, ins.imm, 20)
|
|
wantIntReg(ctxt, ins, "rd", ins.rd)
|
|
wantNoneReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantNoneReg(ctxt, ins, "rs2", ins.rs2)
|
|
wantNoneReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateJ(ctxt *obj.Link, ins *instruction) {
|
|
// Offsets are multiples of two, so accept 21 bit immediates for the
|
|
// 20 bit slot. We implicitly drop the least significant bit in encodeJ.
|
|
wantEvenOffset(ctxt, ins, ins.imm)
|
|
wantImmI(ctxt, ins, ins.imm, 21)
|
|
wantIntReg(ctxt, ins, "rd", ins.rd)
|
|
wantNoneReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantNoneReg(ctxt, ins, "rs2", ins.rs2)
|
|
wantNoneReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateVsetvli(ctxt *obj.Link, ins *instruction) {
|
|
wantImmU(ctxt, ins, ins.imm, 11)
|
|
wantIntReg(ctxt, ins, "rd", ins.rd)
|
|
wantIntReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantNoneReg(ctxt, ins, "rs2", ins.rs2)
|
|
wantNoneReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateVsetivli(ctxt *obj.Link, ins *instruction) {
|
|
wantImmU(ctxt, ins, ins.imm, 10)
|
|
wantIntReg(ctxt, ins, "rd", ins.rd)
|
|
wantImmU(ctxt, ins, int64(ins.rs1), 5)
|
|
wantNoneReg(ctxt, ins, "rs2", ins.rs2)
|
|
wantNoneReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateVsetvl(ctxt *obj.Link, ins *instruction) {
|
|
wantIntReg(ctxt, ins, "rd", ins.rd)
|
|
wantIntReg(ctxt, ins, "rs1", ins.rs1)
|
|
wantIntReg(ctxt, ins, "rs2", ins.rs2)
|
|
wantNoneReg(ctxt, ins, "rs3", ins.rs3)
|
|
}
|
|
|
|
func validateRaw(ctxt *obj.Link, ins *instruction) {
|
|
// Treat the raw value specially as a 32-bit unsigned integer.
|
|
// Nobody wants to enter negative machine code.
|
|
wantImmU(ctxt, ins, ins.imm, 32)
|
|
}
|
|
|
|
// compressedEncoding returns the fixed instruction encoding for a compressed
|
|
// instruction.
|
|
func compressedEncoding(as obj.As) uint32 {
|
|
enc := encode(as)
|
|
if enc == nil {
|
|
panic("compressedEncoding: could not encode instruction")
|
|
}
|
|
|
|
// TODO: this can be removed once encode is reworked to return the
|
|
// necessary bits.
|
|
op := uint32(0)
|
|
switch as {
|
|
case ACSUB:
|
|
op = 0b100011<<10 | 0b00<<5
|
|
case ACXOR:
|
|
op = 0b100011<<10 | 0b01<<5
|
|
case ACOR:
|
|
op = 0b100011<<10 | 0b10<<5
|
|
case ACAND:
|
|
op = 0b100011<<10 | 0b11<<5
|
|
case ACSUBW:
|
|
op = 0b100111<<10 | 0b00<<5
|
|
case ACADDW:
|
|
op = 0b100111<<10 | 0b01<<5
|
|
case ACBEQZ:
|
|
op = 0b110 << 13
|
|
case ACBNEZ:
|
|
op = 0b111 << 13
|
|
case ACANDI:
|
|
op = 0b100<<13 | 0b10<<10
|
|
case ACSRAI:
|
|
op = 0b100<<13 | 0b01<<10
|
|
case ACSRLI:
|
|
op = 0b100<<13 | 0b00<<10
|
|
case ACLI:
|
|
op = 0b010 << 13
|
|
case ACLUI:
|
|
op = 0b011 << 13
|
|
case ACLWSP:
|
|
op = 0b010 << 13
|
|
case ACLDSP:
|
|
op = 0b011 << 13
|
|
case ACFLDSP:
|
|
op = 0b001 << 13
|
|
case ACADDIW:
|
|
op = 0b001 << 13
|
|
case ACADDI16SP:
|
|
op = 0b011 << 13
|
|
case ACADDI4SPN:
|
|
op = 0b000 << 13
|
|
case ACJ:
|
|
op = 0b101 << 13
|
|
case ACLW:
|
|
op = 0b010 << 13
|
|
case ACLD:
|
|
op = 0b011 << 13
|
|
case ACFLD:
|
|
op = 0b001 << 13
|
|
case ACJR:
|
|
op = 0b1000 << 12
|
|
case ACMV:
|
|
op = 0b1000 << 12
|
|
case ACEBREAK:
|
|
op = 0b1001 << 12
|
|
case ACJALR:
|
|
op = 0b1001 << 12
|
|
case ACADD:
|
|
op = 0b1001 << 12
|
|
case ACSW:
|
|
op = 0b110 << 13
|
|
case ACSD:
|
|
op = 0b111 << 13
|
|
case ACFSD:
|
|
op = 0b101 << 13
|
|
case ACSWSP:
|
|
op = 0b110 << 13
|
|
case ACSDSP:
|
|
op = 0b111 << 13
|
|
case ACFSDSP:
|
|
op = 0b101 << 13
|
|
}
|
|
|
|
return op | enc.opcode
|
|
}
|
|
|
|
// encodeBitPattern encodes an immediate value by extracting the specified
|
|
// bit pattern from the given immediate. Each value in the pattern specifies
|
|
// the position of the bit to extract from the immediate, which are then
|
|
// encoded in sequence.
|
|
func encodeBitPattern(imm uint32, pattern []int) uint32 {
|
|
outImm := uint32(0)
|
|
for _, bit := range pattern {
|
|
outImm = outImm<<1 | (imm>>bit)&1
|
|
}
|
|
return outImm
|
|
}
|
|
|
|
// encodeCA encodes a compressed arithmetic (CA-type) instruction.
|
|
func encodeCA(ins *instruction) uint32 {
|
|
return compressedEncoding(ins.as) | regCI(ins.rd)<<7 | regCI(ins.rs2)<<2
|
|
}
|
|
|
|
// encodeCBImmediate encodes an immediate for a CB-type RISC-V instruction.
|
|
func encodeCBImmediate(imm uint32) uint32 {
|
|
// Bit order - [8|4:3|7:6|2:1|5]
|
|
bits := encodeBitPattern(imm, []int{8, 4, 3, 7, 6, 2, 1, 5})
|
|
return (bits>>5)<<10 | (bits&0x1f)<<2
|
|
}
|
|
|
|
// encodeCB encodes a compressed branch (CB-type) instruction.
|
|
func encodeCB(ins *instruction) uint32 {
|
|
imm := uint32(0)
|
|
if ins.as == ACBEQZ || ins.as == ACBNEZ {
|
|
imm = immI(ins.as, ins.imm, 9)
|
|
imm = encodeBitPattern(imm, []int{8, 4, 3, 7, 6, 2, 1, 5})
|
|
} else if ins.as == ACANDI {
|
|
imm = immI(ins.as, ins.imm, 6)
|
|
imm = (imm>>5)<<7 | imm&0x1f
|
|
} else if ins.as == ACSRAI || ins.as == ACSRLI {
|
|
imm = immU(ins.as, ins.imm, 6)
|
|
imm = (imm>>5)<<7 | imm&0x1f
|
|
}
|
|
return compressedEncoding(ins.as) | (imm>>5)<<10 | regCI(ins.rs1)<<7 | (imm&0x1f)<<2
|
|
}
|
|
|
|
// encodeCI encodes a compressed immediate (CI-type) instruction.
|
|
func encodeCI(ins *instruction) uint32 {
|
|
imm := uint32(ins.imm)
|
|
if ins.as == ACLWSP {
|
|
// Bit order [5:2|7:6]
|
|
imm = encodeBitPattern(imm, []int{5, 4, 3, 2, 7, 6})
|
|
} else if ins.as == ACLDSP || ins.as == ACFLDSP {
|
|
// Bit order [5:3|8:6]
|
|
imm = encodeBitPattern(imm, []int{5, 4, 3, 8, 7, 6})
|
|
} else if ins.as == ACADDI16SP {
|
|
// Bit order [9|4|6|8:7|5]
|
|
imm = encodeBitPattern(imm, []int{9, 4, 6, 8, 7, 5})
|
|
}
|
|
rd := uint32(0)
|
|
if ins.as == ACFLDSP {
|
|
rd = regF(ins.rd)
|
|
} else {
|
|
rd = regI(ins.rd)
|
|
}
|
|
return compressedEncoding(ins.as) | ((imm>>5)&0x1)<<12 | rd<<7 | (imm&0x1f)<<2
|
|
}
|
|
|
|
// encodeCIW encodes a compressed immediate wide (CIW-type) instruction.
|
|
func encodeCIW(ins *instruction) uint32 {
|
|
imm := uint32(ins.imm)
|
|
if ins.as == ACADDI4SPN {
|
|
// Bit order [5:4|9:6|2|3]
|
|
imm = encodeBitPattern(imm, []int{5, 4, 9, 8, 7, 6, 2, 3})
|
|
}
|
|
return compressedEncoding(ins.as) | imm<<5 | regCI(ins.rd)<<2
|
|
}
|
|
|
|
// encodeCJImmediate encodes an immediate for a CJ-type RISC-V instruction.
|
|
func encodeCJImmediate(imm uint32) uint32 {
|
|
// Bit order - [11|4|9:8|10|6|7|3:1|5]
|
|
bits := encodeBitPattern(imm, []int{11, 4, 9, 8, 10, 6, 7, 3, 2, 1, 5})
|
|
return bits << 2
|
|
}
|
|
|
|
// encodeCJ encodes a compressed jump (CJ-type) instruction.
|
|
func encodeCJ(ins *instruction) uint32 {
|
|
return compressedEncoding(ins.as) | encodeCJImmediate(uint32(ins.imm))
|
|
}
|
|
|
|
// encodeCL encodes a compressed load (CL-type) instruction.
|
|
func encodeCL(ins *instruction) uint32 {
|
|
imm := uint32(ins.imm)
|
|
if ins.as == ACLW {
|
|
// Bit order [5:2|6]
|
|
imm = encodeBitPattern(imm, []int{5, 4, 3, 2, 6})
|
|
} else if ins.as == ACLD || ins.as == ACFLD {
|
|
// Bit order [5:3|7:6]
|
|
imm = encodeBitPattern(imm, []int{5, 4, 3, 7, 6})
|
|
}
|
|
rd := uint32(0)
|
|
if ins.as == ACFLD {
|
|
rd = regCF(ins.rd)
|
|
} else {
|
|
rd = regCI(ins.rd)
|
|
}
|
|
return compressedEncoding(ins.as) | (imm>>2)<<10 | regCI(ins.rs1)<<7 | (imm&0x3)<<5 | rd<<2
|
|
}
|
|
|
|
// encodeCR encodes a compressed register (CR-type) instruction.
|
|
func encodeCR(ins *instruction) uint32 {
|
|
rs1, rs2 := uint32(0), uint32(0)
|
|
switch ins.as {
|
|
case ACJR, ACJALR:
|
|
rs1 = regI(ins.rs1)
|
|
case ACMV:
|
|
rs1, rs2 = regI(ins.rd), regI(ins.rs2)
|
|
case ACADD:
|
|
rs1, rs2 = regI(ins.rs1), regI(ins.rs2)
|
|
}
|
|
return compressedEncoding(ins.as) | rs1<<7 | rs2<<2
|
|
}
|
|
|
|
// encodeCS encodes a compressed store (CS-type) instruction.
|
|
func encodeCS(ins *instruction) uint32 {
|
|
imm := uint32(ins.imm)
|
|
if ins.as == ACSW {
|
|
// Bit order [5:3|2|6]
|
|
imm = encodeBitPattern(imm, []int{5, 4, 3, 2, 6})
|
|
} else if ins.as == ACSD || ins.as == ACFSD {
|
|
// Bit order [5:3|7:6]
|
|
imm = encodeBitPattern(imm, []int{5, 4, 3, 7, 6})
|
|
}
|
|
rs2 := uint32(0)
|
|
if ins.as == ACFSD {
|
|
rs2 = regCF(ins.rs2)
|
|
} else {
|
|
rs2 = regCI(ins.rs2)
|
|
}
|
|
return compressedEncoding(ins.as) | ((imm>>2)&0x7)<<10 | regCI(ins.rs1)<<7 | (imm&3)<<5 | rs2<<2
|
|
}
|
|
|
|
// encodeCSS encodes a compressed stack-relative store (CSS-type) instruction.
|
|
func encodeCSS(ins *instruction) uint32 {
|
|
imm := uint32(ins.imm)
|
|
if ins.as == ACSWSP {
|
|
// Bit order [5:2|7:6]
|
|
imm = encodeBitPattern(imm, []int{5, 4, 3, 2, 7, 6})
|
|
} else if ins.as == ACSDSP || ins.as == ACFSDSP {
|
|
// Bit order [5:3|8:6]
|
|
imm = encodeBitPattern(imm, []int{5, 4, 3, 8, 7, 6})
|
|
}
|
|
rs2 := uint32(0)
|
|
if ins.as == ACFSDSP {
|
|
rs2 = regF(ins.rs2)
|
|
} else {
|
|
rs2 = regI(ins.rs2)
|
|
}
|
|
return compressedEncoding(ins.as) | imm<<7 | rs2<<2
|
|
}
|
|
|
|
// encodeR encodes an R-type RISC-V instruction.
|
|
func encodeR(as obj.As, rs1, rs2, rd, funct3, funct7 uint32) uint32 {
|
|
enc := encode(as)
|
|
if enc == nil {
|
|
panic("encodeR: could not encode instruction")
|
|
}
|
|
if enc.rs1 != 0 && rs1 != 0 {
|
|
panic("encodeR: instruction uses rs1, but rs1 is nonzero")
|
|
}
|
|
if enc.rs2 != 0 && rs2 != 0 {
|
|
panic("encodeR: instruction uses rs2, but rs2 is nonzero")
|
|
}
|
|
funct3 |= enc.funct3
|
|
funct7 |= enc.funct7
|
|
rs1 |= enc.rs1
|
|
rs2 |= enc.rs2
|
|
return funct7<<25 | rs2<<20 | rs1<<15 | funct3<<12 | rd<<7 | enc.opcode
|
|
}
|
|
|
|
// encodeR4 encodes an R4-type RISC-V instruction.
|
|
func encodeR4(as obj.As, rs1, rs2, rs3, rd, funct3, funct2 uint32) uint32 {
|
|
enc := encode(as)
|
|
if enc == nil {
|
|
panic("encodeR4: could not encode instruction")
|
|
}
|
|
if enc.rs2 != 0 {
|
|
panic("encodeR4: instruction uses rs2")
|
|
}
|
|
funct2 |= enc.funct7
|
|
if funct2&^3 != 0 {
|
|
panic("encodeR4: funct2 requires more than 2 bits")
|
|
}
|
|
return rs3<<27 | funct2<<25 | rs2<<20 | rs1<<15 | enc.funct3<<12 | funct3<<12 | rd<<7 | enc.opcode
|
|
}
|
|
|
|
func encodeRII(ins *instruction) uint32 {
|
|
return encodeR(ins.as, regI(ins.rs1), 0, regI(ins.rd), ins.funct3, ins.funct7)
|
|
}
|
|
|
|
func encodeRIII(ins *instruction) uint32 {
|
|
return encodeR(ins.as, regI(ins.rs1), regI(ins.rs2), regI(ins.rd), ins.funct3, ins.funct7)
|
|
}
|
|
|
|
func encodeRFFF(ins *instruction) uint32 {
|
|
return encodeR(ins.as, regF(ins.rs1), regF(ins.rs2), regF(ins.rd), ins.funct3, ins.funct7)
|
|
}
|
|
|
|
func encodeRFFFF(ins *instruction) uint32 {
|
|
return encodeR4(ins.as, regF(ins.rs1), regF(ins.rs2), regF(ins.rs3), regF(ins.rd), ins.funct3, ins.funct7)
|
|
}
|
|
|
|
func encodeRFFI(ins *instruction) uint32 {
|
|
return encodeR(ins.as, regF(ins.rs1), regF(ins.rs2), regI(ins.rd), ins.funct3, ins.funct7)
|
|
}
|
|
|
|
func encodeRFI(ins *instruction) uint32 {
|
|
return encodeR(ins.as, regF(ins.rs2), 0, regI(ins.rd), ins.funct3, ins.funct7)
|
|
}
|
|
|
|
func encodeRFF(ins *instruction) uint32 {
|
|
return encodeR(ins.as, regF(ins.rs2), 0, regF(ins.rd), ins.funct3, ins.funct7)
|
|
}
|
|
|
|
func encodeRFV(ins *instruction) uint32 {
|
|
return encodeR(ins.as, regF(ins.rs2), 0, regV(ins.rd), ins.funct3, ins.funct7)
|
|
}
|
|
|
|
func encodeRIF(ins *instruction) uint32 {
|
|
return encodeR(ins.as, regI(ins.rs2), 0, regF(ins.rd), ins.funct3, ins.funct7)
|
|
}
|
|
|
|
func encodeRIV(ins *instruction) uint32 {
|
|
return encodeR(ins.as, regI(ins.rs2), 0, regV(ins.rd), ins.funct3, ins.funct7)
|
|
}
|
|
|
|
func encodeRVF(ins *instruction) uint32 {
|
|
return encodeR(ins.as, 0, regV(ins.rs2), regF(ins.rd), ins.funct3, ins.funct7)
|
|
}
|
|
|
|
func encodeRVFV(ins *instruction) uint32 {
|
|
return encodeR(ins.as, regF(ins.rs1), regV(ins.rs2), regV(ins.rd), ins.funct3, ins.funct7)
|
|
}
|
|
|
|
func encodeRVI(ins *instruction) uint32 {
|
|
return encodeR(ins.as, 0, regV(ins.rs2), regI(ins.rd), ins.funct3, ins.funct7)
|
|
}
|
|
|
|
func encodeRVIV(ins *instruction) uint32 {
|
|
return encodeR(ins.as, regI(ins.rs1), regV(ins.rs2), regV(ins.rd), ins.funct3, ins.funct7)
|
|
}
|
|
|
|
func encodeRVV(ins *instruction) uint32 {
|
|
return encodeR(ins.as, 0, regV(ins.rs2), regV(ins.rd), ins.funct3, ins.funct7)
|
|
}
|
|
|
|
func encodeRVVi(ins *instruction) uint32 {
|
|
return encodeR(ins.as, immI(ins.as, ins.imm, 5), regV(ins.rs2), regV(ins.rd), ins.funct3, ins.funct7)
|
|
}
|
|
|
|
func encodeRVVu(ins *instruction) uint32 {
|
|
return encodeR(ins.as, immU(ins.as, ins.imm, 5), regV(ins.rs2), regV(ins.rd), ins.funct3, ins.funct7)
|
|
}
|
|
|
|
func encodeRVVV(ins *instruction) uint32 {
|
|
return encodeR(ins.as, regV(ins.rs1), regV(ins.rs2), regV(ins.rd), ins.funct3, ins.funct7)
|
|
}
|
|
|
|
// encodeI encodes an I-type RISC-V instruction.
|
|
func encodeI(as obj.As, rs1, rd, imm, funct7 uint32) uint32 {
|
|
enc := encode(as)
|
|
if enc == nil {
|
|
panic("encodeI: could not encode instruction")
|
|
}
|
|
imm |= uint32(enc.csr)
|
|
return funct7<<25 | imm<<20 | rs1<<15 | enc.funct3<<12 | rd<<7 | enc.opcode
|
|
}
|
|
|
|
func encodeIII(ins *instruction) uint32 {
|
|
return encodeI(ins.as, regI(ins.rs1), regI(ins.rd), uint32(ins.imm), 0)
|
|
}
|
|
|
|
func encodeIF(ins *instruction) uint32 {
|
|
return encodeI(ins.as, regI(ins.rs1), regF(ins.rd), uint32(ins.imm), 0)
|
|
}
|
|
|
|
func encodeIV(ins *instruction) uint32 {
|
|
return encodeI(ins.as, regI(ins.rs1), regV(ins.rd), uint32(ins.imm), ins.funct7)
|
|
}
|
|
|
|
func encodeIIIV(ins *instruction) uint32 {
|
|
return encodeI(ins.as, regI(ins.rs1), regV(ins.rd), regI(ins.rs2), ins.funct7)
|
|
}
|
|
|
|
func encodeIVIV(ins *instruction) uint32 {
|
|
return encodeI(ins.as, regI(ins.rs1), regV(ins.rd), regV(ins.rs2), ins.funct7)
|
|
}
|
|
|
|
// encodeS encodes an S-type RISC-V instruction.
|
|
func encodeS(as obj.As, rs1, rs2, imm, funct7 uint32) uint32 {
|
|
enc := encode(as)
|
|
if enc == nil {
|
|
panic("encodeS: could not encode instruction")
|
|
}
|
|
if enc.rs2 != 0 && rs2 != 0 {
|
|
panic("encodeS: instruction uses rs2, but rs2 was nonzero")
|
|
}
|
|
rs2 |= enc.rs2
|
|
imm |= uint32(enc.csr) &^ 0x1f
|
|
return funct7<<25 | (imm>>5)<<25 | rs2<<20 | rs1<<15 | enc.funct3<<12 | (imm&0x1f)<<7 | enc.opcode
|
|
}
|
|
|
|
func encodeSI(ins *instruction) uint32 {
|
|
return encodeS(ins.as, regI(ins.rd), regI(ins.rs1), uint32(ins.imm), 0)
|
|
}
|
|
|
|
func encodeSF(ins *instruction) uint32 {
|
|
return encodeS(ins.as, regI(ins.rd), regF(ins.rs1), uint32(ins.imm), 0)
|
|
}
|
|
|
|
func encodeSV(ins *instruction) uint32 {
|
|
return encodeS(ins.as, regI(ins.rd), 0, regV(ins.rs1), ins.funct7)
|
|
}
|
|
|
|
func encodeSVII(ins *instruction) uint32 {
|
|
return encodeS(ins.as, regI(ins.rs1), regI(ins.rs2), regV(ins.rd), ins.funct7)
|
|
}
|
|
|
|
func encodeSVIV(ins *instruction) uint32 {
|
|
return encodeS(ins.as, regI(ins.rs1), regV(ins.rs2), regV(ins.rd), ins.funct7)
|
|
}
|
|
|
|
// encodeBImmediate encodes an immediate for a B-type RISC-V instruction.
|
|
func encodeBImmediate(imm uint32) uint32 {
|
|
return (imm>>12)<<31 | ((imm>>5)&0x3f)<<25 | ((imm>>1)&0xf)<<8 | ((imm>>11)&0x1)<<7
|
|
}
|
|
|
|
// encodeB encodes a B-type RISC-V instruction.
|
|
func encodeB(ins *instruction) uint32 {
|
|
imm := immI(ins.as, ins.imm, 13)
|
|
rs2 := regI(ins.rs1)
|
|
rs1 := regI(ins.rs2)
|
|
enc := encode(ins.as)
|
|
if enc == nil {
|
|
panic("encodeB: could not encode instruction")
|
|
}
|
|
return encodeBImmediate(imm) | rs2<<20 | rs1<<15 | enc.funct3<<12 | enc.opcode
|
|
}
|
|
|
|
// encodeU encodes a U-type RISC-V instruction.
|
|
func encodeU(ins *instruction) uint32 {
|
|
// The immediates for encodeU are the upper 20 bits of a 32 bit value.
|
|
// Rather than have the user/compiler generate a 32 bit constant, the
|
|
// bottommost bits of which must all be zero, instead accept just the
|
|
// top bits.
|
|
imm := immI(ins.as, ins.imm, 20)
|
|
rd := regI(ins.rd)
|
|
enc := encode(ins.as)
|
|
if enc == nil {
|
|
panic("encodeU: could not encode instruction")
|
|
}
|
|
return imm<<12 | rd<<7 | enc.opcode
|
|
}
|
|
|
|
// encodeJImmediate encodes an immediate for a J-type RISC-V instruction.
|
|
func encodeJImmediate(imm uint32) uint32 {
|
|
return (imm>>20)<<31 | ((imm>>1)&0x3ff)<<21 | ((imm>>11)&0x1)<<20 | ((imm>>12)&0xff)<<12
|
|
}
|
|
|
|
// encodeJ encodes a J-type RISC-V instruction.
|
|
func encodeJ(ins *instruction) uint32 {
|
|
imm := immI(ins.as, ins.imm, 21)
|
|
rd := regI(ins.rd)
|
|
enc := encode(ins.as)
|
|
if enc == nil {
|
|
panic("encodeJ: could not encode instruction")
|
|
}
|
|
return encodeJImmediate(imm) | rd<<7 | enc.opcode
|
|
}
|
|
|
|
func encodeVset(as obj.As, rs1, rs2, rd uint32) uint32 {
|
|
enc := encode(as)
|
|
if enc == nil {
|
|
panic("encodeVset: could not encode instruction")
|
|
}
|
|
return enc.funct7<<25 | rs2<<20 | rs1<<15 | enc.funct3<<12 | rd<<7 | enc.opcode
|
|
}
|
|
|
|
func encodeVsetvli(ins *instruction) uint32 {
|
|
vtype := immU(ins.as, ins.imm, 11)
|
|
return encodeVset(ins.as, regI(ins.rs1), vtype, regI(ins.rd))
|
|
}
|
|
|
|
func encodeVsetivli(ins *instruction) uint32 {
|
|
vtype := immU(ins.as, ins.imm, 10)
|
|
avl := immU(ins.as, int64(ins.rs1), 5)
|
|
return encodeVset(ins.as, avl, vtype, regI(ins.rd))
|
|
}
|
|
|
|
func encodeVsetvl(ins *instruction) uint32 {
|
|
return encodeVset(ins.as, regI(ins.rs1), regI(ins.rs2), regI(ins.rd))
|
|
}
|
|
|
|
func encodeRawIns(ins *instruction) uint32 {
|
|
// Treat the raw value specially as a 32-bit unsigned integer.
|
|
// Nobody wants to enter negative machine code.
|
|
return immU(ins.as, ins.imm, 32)
|
|
}
|
|
|
|
func EncodeBImmediate(imm int64) (int64, error) {
|
|
if err := immIFits(imm, 13); err != nil {
|
|
return 0, err
|
|
}
|
|
if err := immEven(imm); err != nil {
|
|
return 0, err
|
|
}
|
|
return int64(encodeBImmediate(uint32(imm))), nil
|
|
}
|
|
|
|
func EncodeCBImmediate(imm int64) (int64, error) {
|
|
if err := immIFits(imm, 9); err != nil {
|
|
return 0, err
|
|
}
|
|
if err := immEven(imm); err != nil {
|
|
return 0, err
|
|
}
|
|
return int64(encodeCBImmediate(uint32(imm))), nil
|
|
}
|
|
|
|
func EncodeCJImmediate(imm int64) (int64, error) {
|
|
if err := immIFits(imm, 12); err != nil {
|
|
return 0, err
|
|
}
|
|
if err := immEven(imm); err != nil {
|
|
return 0, err
|
|
}
|
|
return int64(encodeCJImmediate(uint32(imm))), nil
|
|
}
|
|
|
|
func EncodeIImmediate(imm int64) (int64, error) {
|
|
if err := immIFits(imm, 12); err != nil {
|
|
return 0, err
|
|
}
|
|
return imm << 20, nil
|
|
}
|
|
|
|
func EncodeJImmediate(imm int64) (int64, error) {
|
|
if err := immIFits(imm, 21); err != nil {
|
|
return 0, err
|
|
}
|
|
if err := immEven(imm); err != nil {
|
|
return 0, err
|
|
}
|
|
return int64(encodeJImmediate(uint32(imm))), nil
|
|
}
|
|
|
|
func EncodeSImmediate(imm int64) (int64, error) {
|
|
if err := immIFits(imm, 12); err != nil {
|
|
return 0, err
|
|
}
|
|
return ((imm >> 5) << 25) | ((imm & 0x1f) << 7), nil
|
|
}
|
|
|
|
func EncodeUImmediate(imm int64) (int64, error) {
|
|
if err := immIFits(imm, 20); err != nil {
|
|
return 0, err
|
|
}
|
|
return imm << 12, nil
|
|
}
|
|
|
|
func EncodeVectorType(vsew, vlmul, vtail, vmask int64) (int64, error) {
|
|
vsewSO := SpecialOperand(vsew)
|
|
if vsewSO < SPOP_E8 || vsewSO > SPOP_E64 {
|
|
return -1, fmt.Errorf("invalid vector selected element width %q", vsewSO)
|
|
}
|
|
vlmulSO := SpecialOperand(vlmul)
|
|
if vlmulSO < SPOP_M1 || vlmulSO > SPOP_MF8 {
|
|
return -1, fmt.Errorf("invalid vector register group multiplier %q", vlmulSO)
|
|
}
|
|
vtailSO := SpecialOperand(vtail)
|
|
if vtailSO != SPOP_TA && vtailSO != SPOP_TU {
|
|
return -1, fmt.Errorf("invalid vector tail policy %q", vtailSO)
|
|
}
|
|
vmaskSO := SpecialOperand(vmask)
|
|
if vmaskSO != SPOP_MA && vmaskSO != SPOP_MU {
|
|
return -1, fmt.Errorf("invalid vector mask policy %q", vmaskSO)
|
|
}
|
|
vtype := vmaskSO.encode()<<7 | vtailSO.encode()<<6 | vsewSO.encode()<<3 | vlmulSO.encode()
|
|
return int64(vtype), nil
|
|
}
|
|
|
|
type encoding struct {
|
|
encode func(*instruction) uint32 // encode returns the machine code for an instruction
|
|
validate func(*obj.Link, *instruction) // validate validates an instruction
|
|
length int // length of encoded instruction; 0 for pseudo-ops, 2 for compressed instructions, 4 otherwise
|
|
}
|
|
|
|
var (
|
|
// Encodings have the following naming convention:
|
|
//
|
|
// 1. the instruction encoding (R/I/S/B/U/J), in lowercase
|
|
// 2. zero or more register operand identifiers (I = integer
|
|
// register, F = float register, V = vector register), in uppercase
|
|
// 3. the word "Encoding"
|
|
//
|
|
// For example, rIIIEncoding indicates an R-type instruction with two
|
|
// integer register inputs and an integer register output; sFEncoding
|
|
// indicates an S-type instruction with rs2 being a float register.
|
|
|
|
rIIIEncoding = encoding{encode: encodeRIII, validate: validateRIII, length: 4}
|
|
rIIEncoding = encoding{encode: encodeRII, validate: validateRII, length: 4}
|
|
rFFFEncoding = encoding{encode: encodeRFFF, validate: validateRFFF, length: 4}
|
|
rFFFFEncoding = encoding{encode: encodeRFFFF, validate: validateRFFFF, length: 4}
|
|
rFFIEncoding = encoding{encode: encodeRFFI, validate: validateRFFI, length: 4}
|
|
rFIEncoding = encoding{encode: encodeRFI, validate: validateRFI, length: 4}
|
|
rFVEncoding = encoding{encode: encodeRFV, validate: validateRFV, length: 4}
|
|
rIFEncoding = encoding{encode: encodeRIF, validate: validateRIF, length: 4}
|
|
rIVEncoding = encoding{encode: encodeRIV, validate: validateRIV, length: 4}
|
|
rFFEncoding = encoding{encode: encodeRFF, validate: validateRFF, length: 4}
|
|
rVFEncoding = encoding{encode: encodeRVF, validate: validateRVF, length: 4}
|
|
rVFVEncoding = encoding{encode: encodeRVFV, validate: validateRVFV, length: 4}
|
|
rVIEncoding = encoding{encode: encodeRVI, validate: validateRVI, length: 4}
|
|
rVIVEncoding = encoding{encode: encodeRVIV, validate: validateRVIV, length: 4}
|
|
rVVEncoding = encoding{encode: encodeRVV, validate: validateRVV, length: 4}
|
|
rVViEncoding = encoding{encode: encodeRVVi, validate: validateRVVi, length: 4}
|
|
rVVuEncoding = encoding{encode: encodeRVVu, validate: validateRVVu, length: 4}
|
|
rVVVEncoding = encoding{encode: encodeRVVV, validate: validateRVVV, length: 4}
|
|
|
|
iIIEncoding = encoding{encode: encodeIII, validate: validateIII, length: 4}
|
|
iFEncoding = encoding{encode: encodeIF, validate: validateIF, length: 4}
|
|
iVEncoding = encoding{encode: encodeIV, validate: validateIV, length: 4}
|
|
iIIVEncoding = encoding{encode: encodeIIIV, validate: validateIIIV, length: 4}
|
|
iVIVEncoding = encoding{encode: encodeIVIV, validate: validateIVIV, length: 4}
|
|
|
|
sIEncoding = encoding{encode: encodeSI, validate: validateSI, length: 4}
|
|
sFEncoding = encoding{encode: encodeSF, validate: validateSF, length: 4}
|
|
sVEncoding = encoding{encode: encodeSV, validate: validateSV, length: 4}
|
|
sVIIEncoding = encoding{encode: encodeSVII, validate: validateSVII, length: 4}
|
|
sVIVEncoding = encoding{encode: encodeSVIV, validate: validateSVIV, length: 4}
|
|
|
|
bEncoding = encoding{encode: encodeB, validate: validateB, length: 4}
|
|
uEncoding = encoding{encode: encodeU, validate: validateU, length: 4}
|
|
jEncoding = encoding{encode: encodeJ, validate: validateJ, length: 4}
|
|
|
|
// Compressed encodings.
|
|
caEncoding = encoding{encode: encodeCA, validate: validateCA, length: 2}
|
|
cbEncoding = encoding{encode: encodeCB, validate: validateCB, length: 2}
|
|
ciEncoding = encoding{encode: encodeCI, validate: validateCI, length: 2}
|
|
ciwEncoding = encoding{encode: encodeCIW, validate: validateCIW, length: 2}
|
|
cjEncoding = encoding{encode: encodeCJ, validate: validateCJ, length: 2}
|
|
clEncoding = encoding{encode: encodeCL, validate: validateCL, length: 2}
|
|
crEncoding = encoding{encode: encodeCR, validate: validateCR, length: 2}
|
|
csEncoding = encoding{encode: encodeCS, validate: validateCS, length: 2}
|
|
cssEncoding = encoding{encode: encodeCSS, validate: validateCSS, length: 2}
|
|
|
|
// Encodings for vector configuration setting instruction.
|
|
vsetvliEncoding = encoding{encode: encodeVsetvli, validate: validateVsetvli, length: 4}
|
|
vsetivliEncoding = encoding{encode: encodeVsetivli, validate: validateVsetivli, length: 4}
|
|
vsetvlEncoding = encoding{encode: encodeVsetvl, validate: validateVsetvl, length: 4}
|
|
|
|
// rawEncoding encodes a raw instruction byte sequence.
|
|
rawEncoding = encoding{encode: encodeRawIns, validate: validateRaw, length: 4}
|
|
|
|
// pseudoOpEncoding panics if encoding is attempted, but does no validation.
|
|
pseudoOpEncoding = encoding{encode: nil, validate: func(*obj.Link, *instruction) {}, length: 0}
|
|
|
|
// badEncoding is used when an invalid op is encountered.
|
|
// An error has already been generated, so let anything else through.
|
|
badEncoding = encoding{encode: func(*instruction) uint32 { return 0 }, validate: func(*obj.Link, *instruction) {}, length: 0}
|
|
)
|
|
|
|
// instructionData specifies details relating to a RISC-V instruction.
|
|
type instructionData struct {
|
|
enc encoding
|
|
immForm obj.As // immediate form of this instruction
|
|
ternary bool
|
|
}
|
|
|
|
// instructions contains details of RISC-V instructions, including
|
|
// their encoding type. Entries are masked with obj.AMask to keep
|
|
// indices small.
|
|
var instructions = [ALAST & obj.AMask]instructionData{
|
|
//
|
|
// Unprivileged ISA
|
|
//
|
|
|
|
// 2.4: Integer Computational Instructions
|
|
AADDI & obj.AMask: {enc: iIIEncoding, ternary: true},
|
|
ASLTI & obj.AMask: {enc: iIIEncoding, ternary: true},
|
|
ASLTIU & obj.AMask: {enc: iIIEncoding, ternary: true},
|
|
AANDI & obj.AMask: {enc: iIIEncoding, ternary: true},
|
|
AORI & obj.AMask: {enc: iIIEncoding, ternary: true},
|
|
AXORI & obj.AMask: {enc: iIIEncoding, ternary: true},
|
|
ASLLI & obj.AMask: {enc: iIIEncoding, ternary: true},
|
|
ASRLI & obj.AMask: {enc: iIIEncoding, ternary: true},
|
|
ASRAI & obj.AMask: {enc: iIIEncoding, ternary: true},
|
|
ALUI & obj.AMask: {enc: uEncoding},
|
|
AAUIPC & obj.AMask: {enc: uEncoding},
|
|
AADD & obj.AMask: {enc: rIIIEncoding, immForm: AADDI, ternary: true},
|
|
ASLT & obj.AMask: {enc: rIIIEncoding, immForm: ASLTI, ternary: true},
|
|
ASLTU & obj.AMask: {enc: rIIIEncoding, immForm: ASLTIU, ternary: true},
|
|
AAND & obj.AMask: {enc: rIIIEncoding, immForm: AANDI, ternary: true},
|
|
AOR & obj.AMask: {enc: rIIIEncoding, immForm: AORI, ternary: true},
|
|
AXOR & obj.AMask: {enc: rIIIEncoding, immForm: AXORI, ternary: true},
|
|
ASLL & obj.AMask: {enc: rIIIEncoding, immForm: ASLLI, ternary: true},
|
|
ASRL & obj.AMask: {enc: rIIIEncoding, immForm: ASRLI, ternary: true},
|
|
ASUB & obj.AMask: {enc: rIIIEncoding, ternary: true},
|
|
ASRA & obj.AMask: {enc: rIIIEncoding, immForm: ASRAI, ternary: true},
|
|
|
|
// 2.5: Control Transfer Instructions
|
|
AJAL & obj.AMask: {enc: jEncoding},
|
|
AJALR & obj.AMask: {enc: iIIEncoding},
|
|
ABEQ & obj.AMask: {enc: bEncoding},
|
|
ABNE & obj.AMask: {enc: bEncoding},
|
|
ABLT & obj.AMask: {enc: bEncoding},
|
|
ABLTU & obj.AMask: {enc: bEncoding},
|
|
ABGE & obj.AMask: {enc: bEncoding},
|
|
ABGEU & obj.AMask: {enc: bEncoding},
|
|
|
|
// 2.6: Load and Store Instructions
|
|
ALW & obj.AMask: {enc: iIIEncoding},
|
|
ALWU & obj.AMask: {enc: iIIEncoding},
|
|
ALH & obj.AMask: {enc: iIIEncoding},
|
|
ALHU & obj.AMask: {enc: iIIEncoding},
|
|
ALB & obj.AMask: {enc: iIIEncoding},
|
|
ALBU & obj.AMask: {enc: iIIEncoding},
|
|
ASW & obj.AMask: {enc: sIEncoding},
|
|
ASH & obj.AMask: {enc: sIEncoding},
|
|
ASB & obj.AMask: {enc: sIEncoding},
|
|
|
|
// 2.7: Memory Ordering
|
|
AFENCE & obj.AMask: {enc: iIIEncoding},
|
|
|
|
// 4.2: Integer Computational Instructions (RV64I)
|
|
AADDIW & obj.AMask: {enc: iIIEncoding, ternary: true},
|
|
ASLLIW & obj.AMask: {enc: iIIEncoding, ternary: true},
|
|
ASRLIW & obj.AMask: {enc: iIIEncoding, ternary: true},
|
|
ASRAIW & obj.AMask: {enc: iIIEncoding, ternary: true},
|
|
AADDW & obj.AMask: {enc: rIIIEncoding, immForm: AADDIW, ternary: true},
|
|
ASLLW & obj.AMask: {enc: rIIIEncoding, immForm: ASLLIW, ternary: true},
|
|
ASRLW & obj.AMask: {enc: rIIIEncoding, immForm: ASRLIW, ternary: true},
|
|
ASUBW & obj.AMask: {enc: rIIIEncoding, ternary: true},
|
|
ASRAW & obj.AMask: {enc: rIIIEncoding, immForm: ASRAIW, ternary: true},
|
|
|
|
// 4.3: Load and Store Instructions (RV64I)
|
|
ALD & obj.AMask: {enc: iIIEncoding},
|
|
ASD & obj.AMask: {enc: sIEncoding},
|
|
|
|
// 7.1: CSR Instructions
|
|
ACSRRC & obj.AMask: {enc: iIIEncoding, immForm: ACSRRCI},
|
|
ACSRRCI & obj.AMask: {enc: iIIEncoding},
|
|
ACSRRS & obj.AMask: {enc: iIIEncoding, immForm: ACSRRSI},
|
|
ACSRRSI & obj.AMask: {enc: iIIEncoding},
|
|
ACSRRW & obj.AMask: {enc: iIIEncoding, immForm: ACSRRWI},
|
|
ACSRRWI & obj.AMask: {enc: iIIEncoding},
|
|
|
|
// 12.3: "Zicond" Extension for Integer Conditional Operations
|
|
ACZERONEZ & obj.AMask: {enc: rIIIEncoding, ternary: true},
|
|
ACZEROEQZ & obj.AMask: {enc: rIIIEncoding, ternary: true},
|
|
|
|
// 13.1: Multiplication Operations
|
|
AMUL & obj.AMask: {enc: rIIIEncoding, ternary: true},
|
|
AMULH & obj.AMask: {enc: rIIIEncoding, ternary: true},
|
|
AMULHU & obj.AMask: {enc: rIIIEncoding, ternary: true},
|
|
AMULHSU & obj.AMask: {enc: rIIIEncoding, ternary: true},
|
|
AMULW & obj.AMask: {enc: rIIIEncoding, ternary: true},
|
|
ADIV & obj.AMask: {enc: rIIIEncoding, ternary: true},
|
|
ADIVU & obj.AMask: {enc: rIIIEncoding, ternary: true},
|
|
AREM & obj.AMask: {enc: rIIIEncoding, ternary: true},
|
|
AREMU & obj.AMask: {enc: rIIIEncoding, ternary: true},
|
|
ADIVW & obj.AMask: {enc: rIIIEncoding, ternary: true},
|
|
ADIVUW & obj.AMask: {enc: rIIIEncoding, ternary: true},
|
|
AREMW & obj.AMask: {enc: rIIIEncoding, ternary: true},
|
|
AREMUW & obj.AMask: {enc: rIIIEncoding, ternary: true},
|
|
|
|
// 14.2: Load-Reserved/Store-Conditional Instructions (Zalrsc)
|
|
ALRW & obj.AMask: {enc: rIIIEncoding},
|
|
ALRD & obj.AMask: {enc: rIIIEncoding},
|
|
ASCW & obj.AMask: {enc: rIIIEncoding},
|
|
ASCD & obj.AMask: {enc: rIIIEncoding},
|
|
|
|
// 14.4: Atomic Memory Operations (Zaamo)
|
|
AAMOSWAPW & obj.AMask: {enc: rIIIEncoding},
|
|
AAMOSWAPD & obj.AMask: {enc: rIIIEncoding},
|
|
AAMOADDW & obj.AMask: {enc: rIIIEncoding},
|
|
AAMOADDD & obj.AMask: {enc: rIIIEncoding},
|
|
AAMOANDW & obj.AMask: {enc: rIIIEncoding},
|
|
AAMOANDD & obj.AMask: {enc: rIIIEncoding},
|
|
AAMOORW & obj.AMask: {enc: rIIIEncoding},
|
|
AAMOORD & obj.AMask: {enc: rIIIEncoding},
|
|
AAMOXORW & obj.AMask: {enc: rIIIEncoding},
|
|
AAMOXORD & obj.AMask: {enc: rIIIEncoding},
|
|
AAMOMAXW & obj.AMask: {enc: rIIIEncoding},
|
|
AAMOMAXD & obj.AMask: {enc: rIIIEncoding},
|
|
AAMOMAXUW & obj.AMask: {enc: rIIIEncoding},
|
|
AAMOMAXUD & obj.AMask: {enc: rIIIEncoding},
|
|
AAMOMINW & obj.AMask: {enc: rIIIEncoding},
|
|
AAMOMIND & obj.AMask: {enc: rIIIEncoding},
|
|
AAMOMINUW & obj.AMask: {enc: rIIIEncoding},
|
|
AAMOMINUD & obj.AMask: {enc: rIIIEncoding},
|
|
|
|
// 20.5: Single-Precision Load and Store Instructions
|
|
AFLW & obj.AMask: {enc: iFEncoding},
|
|
AFSW & obj.AMask: {enc: sFEncoding},
|
|
|
|
// 20.6: Single-Precision Floating-Point Computational Instructions
|
|
AFADDS & obj.AMask: {enc: rFFFEncoding},
|
|
AFSUBS & obj.AMask: {enc: rFFFEncoding},
|
|
AFMULS & obj.AMask: {enc: rFFFEncoding},
|
|
AFDIVS & obj.AMask: {enc: rFFFEncoding},
|
|
AFMINS & obj.AMask: {enc: rFFFEncoding},
|
|
AFMAXS & obj.AMask: {enc: rFFFEncoding},
|
|
AFSQRTS & obj.AMask: {enc: rFFFEncoding},
|
|
AFMADDS & obj.AMask: {enc: rFFFFEncoding},
|
|
AFMSUBS & obj.AMask: {enc: rFFFFEncoding},
|
|
AFNMSUBS & obj.AMask: {enc: rFFFFEncoding},
|
|
AFNMADDS & obj.AMask: {enc: rFFFFEncoding},
|
|
|
|
// 20.7: Single-Precision Floating-Point Conversion and Move Instructions
|
|
AFCVTWS & obj.AMask: {enc: rFIEncoding},
|
|
AFCVTLS & obj.AMask: {enc: rFIEncoding},
|
|
AFCVTSW & obj.AMask: {enc: rIFEncoding},
|
|
AFCVTSL & obj.AMask: {enc: rIFEncoding},
|
|
AFCVTWUS & obj.AMask: {enc: rFIEncoding},
|
|
AFCVTLUS & obj.AMask: {enc: rFIEncoding},
|
|
AFCVTSWU & obj.AMask: {enc: rIFEncoding},
|
|
AFCVTSLU & obj.AMask: {enc: rIFEncoding},
|
|
AFSGNJS & obj.AMask: {enc: rFFFEncoding},
|
|
AFSGNJNS & obj.AMask: {enc: rFFFEncoding},
|
|
AFSGNJXS & obj.AMask: {enc: rFFFEncoding},
|
|
AFMVXW & obj.AMask: {enc: rFIEncoding},
|
|
AFMVWX & obj.AMask: {enc: rIFEncoding},
|
|
|
|
// 20.8: Single-Precision Floating-Point Compare Instructions
|
|
AFEQS & obj.AMask: {enc: rFFIEncoding},
|
|
AFLTS & obj.AMask: {enc: rFFIEncoding},
|
|
AFLES & obj.AMask: {enc: rFFIEncoding},
|
|
|
|
// 20.9: Single-Precision Floating-Point Classify Instruction
|
|
AFCLASSS & obj.AMask: {enc: rFIEncoding},
|
|
|
|
// 12.3: Double-Precision Load and Store Instructions
|
|
AFLD & obj.AMask: {enc: iFEncoding},
|
|
AFSD & obj.AMask: {enc: sFEncoding},
|
|
|
|
// 21.4: Double-Precision Floating-Point Computational Instructions
|
|
AFADDD & obj.AMask: {enc: rFFFEncoding},
|
|
AFSUBD & obj.AMask: {enc: rFFFEncoding},
|
|
AFMULD & obj.AMask: {enc: rFFFEncoding},
|
|
AFDIVD & obj.AMask: {enc: rFFFEncoding},
|
|
AFMIND & obj.AMask: {enc: rFFFEncoding},
|
|
AFMAXD & obj.AMask: {enc: rFFFEncoding},
|
|
AFSQRTD & obj.AMask: {enc: rFFFEncoding},
|
|
AFMADDD & obj.AMask: {enc: rFFFFEncoding},
|
|
AFMSUBD & obj.AMask: {enc: rFFFFEncoding},
|
|
AFNMSUBD & obj.AMask: {enc: rFFFFEncoding},
|
|
AFNMADDD & obj.AMask: {enc: rFFFFEncoding},
|
|
|
|
// 21.5: Double-Precision Floating-Point Conversion and Move Instructions
|
|
AFCVTWD & obj.AMask: {enc: rFIEncoding},
|
|
AFCVTLD & obj.AMask: {enc: rFIEncoding},
|
|
AFCVTDW & obj.AMask: {enc: rIFEncoding},
|
|
AFCVTDL & obj.AMask: {enc: rIFEncoding},
|
|
AFCVTWUD & obj.AMask: {enc: rFIEncoding},
|
|
AFCVTLUD & obj.AMask: {enc: rFIEncoding},
|
|
AFCVTDWU & obj.AMask: {enc: rIFEncoding},
|
|
AFCVTDLU & obj.AMask: {enc: rIFEncoding},
|
|
AFCVTSD & obj.AMask: {enc: rFFEncoding},
|
|
AFCVTDS & obj.AMask: {enc: rFFEncoding},
|
|
AFSGNJD & obj.AMask: {enc: rFFFEncoding},
|
|
AFSGNJND & obj.AMask: {enc: rFFFEncoding},
|
|
AFSGNJXD & obj.AMask: {enc: rFFFEncoding},
|
|
AFMVXD & obj.AMask: {enc: rFIEncoding},
|
|
AFMVDX & obj.AMask: {enc: rIFEncoding},
|
|
|
|
// 21.6: Double-Precision Floating-Point Compare Instructions
|
|
AFEQD & obj.AMask: {enc: rFFIEncoding},
|
|
AFLTD & obj.AMask: {enc: rFFIEncoding},
|
|
AFLED & obj.AMask: {enc: rFFIEncoding},
|
|
|
|
// 21.7: Double-Precision Floating-Point Classify Instruction
|
|
AFCLASSD & obj.AMask: {enc: rFIEncoding},
|
|
|
|
//
|
|
// "C" Extension for Compressed Instructions, Version 2.0
|
|
//
|
|
|
|
// 26.3.1: Compressed Stack-Pointer-Based Loads and Stores
|
|
ACLWSP & obj.AMask: {enc: ciEncoding},
|
|
ACLDSP & obj.AMask: {enc: ciEncoding},
|
|
ACFLDSP & obj.AMask: {enc: ciEncoding},
|
|
ACSWSP & obj.AMask: {enc: cssEncoding},
|
|
ACSDSP & obj.AMask: {enc: cssEncoding},
|
|
ACFSDSP & obj.AMask: {enc: cssEncoding},
|
|
|
|
// 26.3.2: Compressed Register-Based Loads and Stores
|
|
ACLW & obj.AMask: {enc: clEncoding},
|
|
ACLD & obj.AMask: {enc: clEncoding},
|
|
ACFLD & obj.AMask: {enc: clEncoding},
|
|
ACSW & obj.AMask: {enc: csEncoding},
|
|
ACSD & obj.AMask: {enc: csEncoding},
|
|
ACFSD & obj.AMask: {enc: csEncoding},
|
|
|
|
// 26.4: Compressed Control Transfer Instructions
|
|
ACJ & obj.AMask: {enc: cjEncoding},
|
|
ACJR & obj.AMask: {enc: crEncoding},
|
|
ACJALR & obj.AMask: {enc: crEncoding},
|
|
ACBEQZ & obj.AMask: {enc: cbEncoding},
|
|
ACBNEZ & obj.AMask: {enc: cbEncoding},
|
|
|
|
// 26.5.1: Compressed Integer Constant-Generation Instructions
|
|
ACLI & obj.AMask: {enc: ciEncoding},
|
|
ACLUI & obj.AMask: {enc: ciEncoding},
|
|
|
|
// 26.5.2: Compressed Integer Register-Immediate Operations
|
|
ACADDI & obj.AMask: {enc: ciEncoding, ternary: true},
|
|
ACADDIW & obj.AMask: {enc: ciEncoding, ternary: true},
|
|
ACADDI16SP & obj.AMask: {enc: ciEncoding, ternary: true},
|
|
ACADDI4SPN & obj.AMask: {enc: ciwEncoding, ternary: true},
|
|
ACSLLI & obj.AMask: {enc: ciEncoding, ternary: true},
|
|
ACSRLI & obj.AMask: {enc: cbEncoding, ternary: true},
|
|
ACSRAI & obj.AMask: {enc: cbEncoding, ternary: true},
|
|
ACANDI & obj.AMask: {enc: cbEncoding, ternary: true},
|
|
|
|
// 26.5.3: Compressed Integer Register-Register Operations
|
|
ACMV & obj.AMask: {enc: crEncoding},
|
|
ACADD & obj.AMask: {enc: crEncoding, immForm: ACADDI, ternary: true},
|
|
ACAND & obj.AMask: {enc: caEncoding, immForm: ACANDI, ternary: true},
|
|
ACOR & obj.AMask: {enc: caEncoding, ternary: true},
|
|
ACXOR & obj.AMask: {enc: caEncoding, ternary: true},
|
|
ACSUB & obj.AMask: {enc: caEncoding, ternary: true},
|
|
ACADDW & obj.AMask: {enc: caEncoding, immForm: ACADDIW, ternary: true},
|
|
ACSUBW & obj.AMask: {enc: caEncoding, ternary: true},
|
|
|
|
// 26.5.5: Compressed NOP Instruction
|
|
ACNOP & obj.AMask: {enc: ciEncoding},
|
|
|
|
// 26.5.6: Compressed Breakpoint Instruction
|
|
ACEBREAK & obj.AMask: {enc: crEncoding},
|
|
|
|
//
|
|
// "B" Extension for Bit Manipulation, Version 1.0.0
|
|
//
|
|
|
|
// 28.4.1: Address Generation Instructions (Zba)
|
|
AADDUW & obj.AMask: {enc: rIIIEncoding, ternary: true},
|
|
ASH1ADD & obj.AMask: {enc: rIIIEncoding, ternary: true},
|
|
ASH1ADDUW & obj.AMask: {enc: rIIIEncoding, ternary: true},
|
|
ASH2ADD & obj.AMask: {enc: rIIIEncoding, ternary: true},
|
|
ASH2ADDUW & obj.AMask: {enc: rIIIEncoding, ternary: true},
|
|
ASH3ADD & obj.AMask: {enc: rIIIEncoding, ternary: true},
|
|
ASH3ADDUW & obj.AMask: {enc: rIIIEncoding, ternary: true},
|
|
ASLLIUW & obj.AMask: {enc: iIIEncoding, ternary: true},
|
|
|
|
// 28.4.2: Basic Bit Manipulation (Zbb)
|
|
AANDN & obj.AMask: {enc: rIIIEncoding, ternary: true},
|
|
ACLZ & obj.AMask: {enc: rIIEncoding},
|
|
ACLZW & obj.AMask: {enc: rIIEncoding},
|
|
ACPOP & obj.AMask: {enc: rIIEncoding},
|
|
ACPOPW & obj.AMask: {enc: rIIEncoding},
|
|
ACTZ & obj.AMask: {enc: rIIEncoding},
|
|
ACTZW & obj.AMask: {enc: rIIEncoding},
|
|
AMAX & obj.AMask: {enc: rIIIEncoding, ternary: true},
|
|
AMAXU & obj.AMask: {enc: rIIIEncoding, ternary: true},
|
|
AMIN & obj.AMask: {enc: rIIIEncoding, ternary: true},
|
|
AMINU & obj.AMask: {enc: rIIIEncoding, ternary: true},
|
|
AORN & obj.AMask: {enc: rIIIEncoding, ternary: true},
|
|
ASEXTB & obj.AMask: {enc: rIIEncoding},
|
|
ASEXTH & obj.AMask: {enc: rIIEncoding},
|
|
AXNOR & obj.AMask: {enc: rIIIEncoding, ternary: true},
|
|
AZEXTH & obj.AMask: {enc: rIIEncoding},
|
|
|
|
// 28.4.3: Bitwise Rotation (Zbb)
|
|
AROL & obj.AMask: {enc: rIIIEncoding, ternary: true},
|
|
AROLW & obj.AMask: {enc: rIIIEncoding, ternary: true},
|
|
AROR & obj.AMask: {enc: rIIIEncoding, immForm: ARORI, ternary: true},
|
|
ARORI & obj.AMask: {enc: iIIEncoding, ternary: true},
|
|
ARORIW & obj.AMask: {enc: iIIEncoding, ternary: true},
|
|
ARORW & obj.AMask: {enc: rIIIEncoding, immForm: ARORIW, ternary: true},
|
|
AORCB & obj.AMask: {enc: rIIEncoding},
|
|
AREV8 & obj.AMask: {enc: rIIEncoding},
|
|
|
|
// 28.4.4: Single-bit Instructions (Zbs)
|
|
ABCLR & obj.AMask: {enc: rIIIEncoding, immForm: ABCLRI, ternary: true},
|
|
ABCLRI & obj.AMask: {enc: iIIEncoding, ternary: true},
|
|
ABEXT & obj.AMask: {enc: rIIIEncoding, immForm: ABEXTI, ternary: true},
|
|
ABEXTI & obj.AMask: {enc: iIIEncoding, ternary: true},
|
|
ABINV & obj.AMask: {enc: rIIIEncoding, immForm: ABINVI, ternary: true},
|
|
ABINVI & obj.AMask: {enc: iIIEncoding, ternary: true},
|
|
ABSET & obj.AMask: {enc: rIIIEncoding, immForm: ABSETI, ternary: true},
|
|
ABSETI & obj.AMask: {enc: iIIEncoding, ternary: true},
|
|
|
|
//
|
|
// "V" Standard Extension for Vector Operations, Version 1.0
|
|
//
|
|
|
|
// 31.6: Vector Configuration-Setting Instructions
|
|
AVSETVLI & obj.AMask: {enc: vsetvliEncoding, immForm: AVSETIVLI},
|
|
AVSETIVLI & obj.AMask: {enc: vsetivliEncoding},
|
|
AVSETVL & obj.AMask: {enc: vsetvlEncoding},
|
|
|
|
// 31.7.4: Vector Unit-Stride Instructions
|
|
AVLE8V & obj.AMask: {enc: iVEncoding},
|
|
AVLE16V & obj.AMask: {enc: iVEncoding},
|
|
AVLE32V & obj.AMask: {enc: iVEncoding},
|
|
AVLE64V & obj.AMask: {enc: iVEncoding},
|
|
AVSE8V & obj.AMask: {enc: sVEncoding},
|
|
AVSE16V & obj.AMask: {enc: sVEncoding},
|
|
AVSE32V & obj.AMask: {enc: sVEncoding},
|
|
AVSE64V & obj.AMask: {enc: sVEncoding},
|
|
AVLMV & obj.AMask: {enc: iVEncoding},
|
|
AVSMV & obj.AMask: {enc: sVEncoding},
|
|
|
|
// 31.7.5: Vector Strided Instructions
|
|
AVLSE8V & obj.AMask: {enc: iIIVEncoding},
|
|
AVLSE16V & obj.AMask: {enc: iIIVEncoding},
|
|
AVLSE32V & obj.AMask: {enc: iIIVEncoding},
|
|
AVLSE64V & obj.AMask: {enc: iIIVEncoding},
|
|
AVSSE8V & obj.AMask: {enc: sVIIEncoding},
|
|
AVSSE16V & obj.AMask: {enc: sVIIEncoding},
|
|
AVSSE32V & obj.AMask: {enc: sVIIEncoding},
|
|
AVSSE64V & obj.AMask: {enc: sVIIEncoding},
|
|
|
|
// 31.7.6: Vector Indexed Instructions
|
|
AVLUXEI8V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLUXEI16V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLUXEI32V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLUXEI64V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLOXEI8V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLOXEI16V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLOXEI32V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLOXEI64V & obj.AMask: {enc: iVIVEncoding},
|
|
AVSUXEI8V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSUXEI16V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSUXEI32V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSUXEI64V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSOXEI8V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSOXEI16V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSOXEI32V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSOXEI64V & obj.AMask: {enc: sVIVEncoding},
|
|
|
|
// 31.7.7: Unit-stride Fault-Only-First Loads
|
|
AVLE8FFV & obj.AMask: {enc: iVEncoding},
|
|
AVLE16FFV & obj.AMask: {enc: iVEncoding},
|
|
AVLE32FFV & obj.AMask: {enc: iVEncoding},
|
|
AVLE64FFV & obj.AMask: {enc: iVEncoding},
|
|
|
|
// 31.7.8: Vector Load/Store Segment Instructions
|
|
AVLSEG2E8V & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG3E8V & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG4E8V & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG5E8V & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG6E8V & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG7E8V & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG8E8V & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG2E16V & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG3E16V & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG4E16V & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG5E16V & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG6E16V & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG7E16V & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG8E16V & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG2E32V & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG3E32V & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG4E32V & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG5E32V & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG6E32V & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG7E32V & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG8E32V & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG2E64V & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG3E64V & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG4E64V & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG5E64V & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG6E64V & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG7E64V & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG8E64V & obj.AMask: {enc: iVEncoding},
|
|
AVSSEG2E8V & obj.AMask: {enc: sVEncoding},
|
|
AVSSEG3E8V & obj.AMask: {enc: sVEncoding},
|
|
AVSSEG4E8V & obj.AMask: {enc: sVEncoding},
|
|
AVSSEG5E8V & obj.AMask: {enc: sVEncoding},
|
|
AVSSEG6E8V & obj.AMask: {enc: sVEncoding},
|
|
AVSSEG7E8V & obj.AMask: {enc: sVEncoding},
|
|
AVSSEG8E8V & obj.AMask: {enc: sVEncoding},
|
|
AVSSEG2E16V & obj.AMask: {enc: sVEncoding},
|
|
AVSSEG3E16V & obj.AMask: {enc: sVEncoding},
|
|
AVSSEG4E16V & obj.AMask: {enc: sVEncoding},
|
|
AVSSEG5E16V & obj.AMask: {enc: sVEncoding},
|
|
AVSSEG6E16V & obj.AMask: {enc: sVEncoding},
|
|
AVSSEG7E16V & obj.AMask: {enc: sVEncoding},
|
|
AVSSEG8E16V & obj.AMask: {enc: sVEncoding},
|
|
AVSSEG2E32V & obj.AMask: {enc: sVEncoding},
|
|
AVSSEG3E32V & obj.AMask: {enc: sVEncoding},
|
|
AVSSEG4E32V & obj.AMask: {enc: sVEncoding},
|
|
AVSSEG5E32V & obj.AMask: {enc: sVEncoding},
|
|
AVSSEG6E32V & obj.AMask: {enc: sVEncoding},
|
|
AVSSEG7E32V & obj.AMask: {enc: sVEncoding},
|
|
AVSSEG8E32V & obj.AMask: {enc: sVEncoding},
|
|
AVSSEG2E64V & obj.AMask: {enc: sVEncoding},
|
|
AVSSEG3E64V & obj.AMask: {enc: sVEncoding},
|
|
AVSSEG4E64V & obj.AMask: {enc: sVEncoding},
|
|
AVSSEG5E64V & obj.AMask: {enc: sVEncoding},
|
|
AVSSEG6E64V & obj.AMask: {enc: sVEncoding},
|
|
AVSSEG7E64V & obj.AMask: {enc: sVEncoding},
|
|
AVSSEG8E64V & obj.AMask: {enc: sVEncoding},
|
|
AVLSEG2E8FFV & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG3E8FFV & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG4E8FFV & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG5E8FFV & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG6E8FFV & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG7E8FFV & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG8E8FFV & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG2E16FFV & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG3E16FFV & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG4E16FFV & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG5E16FFV & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG6E16FFV & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG7E16FFV & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG8E16FFV & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG2E32FFV & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG3E32FFV & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG4E32FFV & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG5E32FFV & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG6E32FFV & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG7E32FFV & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG8E32FFV & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG2E64FFV & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG3E64FFV & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG4E64FFV & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG5E64FFV & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG6E64FFV & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG7E64FFV & obj.AMask: {enc: iVEncoding},
|
|
AVLSEG8E64FFV & obj.AMask: {enc: iVEncoding},
|
|
AVLSSEG2E8V & obj.AMask: {enc: iIIVEncoding},
|
|
AVLSSEG3E8V & obj.AMask: {enc: iIIVEncoding},
|
|
AVLSSEG4E8V & obj.AMask: {enc: iIIVEncoding},
|
|
AVLSSEG5E8V & obj.AMask: {enc: iIIVEncoding},
|
|
AVLSSEG6E8V & obj.AMask: {enc: iIIVEncoding},
|
|
AVLSSEG7E8V & obj.AMask: {enc: iIIVEncoding},
|
|
AVLSSEG8E8V & obj.AMask: {enc: iIIVEncoding},
|
|
AVLSSEG2E16V & obj.AMask: {enc: iIIVEncoding},
|
|
AVLSSEG3E16V & obj.AMask: {enc: iIIVEncoding},
|
|
AVLSSEG4E16V & obj.AMask: {enc: iIIVEncoding},
|
|
AVLSSEG5E16V & obj.AMask: {enc: iIIVEncoding},
|
|
AVLSSEG6E16V & obj.AMask: {enc: iIIVEncoding},
|
|
AVLSSEG7E16V & obj.AMask: {enc: iIIVEncoding},
|
|
AVLSSEG8E16V & obj.AMask: {enc: iIIVEncoding},
|
|
AVLSSEG2E32V & obj.AMask: {enc: iIIVEncoding},
|
|
AVLSSEG3E32V & obj.AMask: {enc: iIIVEncoding},
|
|
AVLSSEG4E32V & obj.AMask: {enc: iIIVEncoding},
|
|
AVLSSEG5E32V & obj.AMask: {enc: iIIVEncoding},
|
|
AVLSSEG6E32V & obj.AMask: {enc: iIIVEncoding},
|
|
AVLSSEG7E32V & obj.AMask: {enc: iIIVEncoding},
|
|
AVLSSEG8E32V & obj.AMask: {enc: iIIVEncoding},
|
|
AVLSSEG2E64V & obj.AMask: {enc: iIIVEncoding},
|
|
AVLSSEG3E64V & obj.AMask: {enc: iIIVEncoding},
|
|
AVLSSEG4E64V & obj.AMask: {enc: iIIVEncoding},
|
|
AVLSSEG5E64V & obj.AMask: {enc: iIIVEncoding},
|
|
AVLSSEG6E64V & obj.AMask: {enc: iIIVEncoding},
|
|
AVLSSEG7E64V & obj.AMask: {enc: iIIVEncoding},
|
|
AVLSSEG8E64V & obj.AMask: {enc: iIIVEncoding},
|
|
AVSSSEG2E8V & obj.AMask: {enc: sVIIEncoding},
|
|
AVSSSEG3E8V & obj.AMask: {enc: sVIIEncoding},
|
|
AVSSSEG4E8V & obj.AMask: {enc: sVIIEncoding},
|
|
AVSSSEG5E8V & obj.AMask: {enc: sVIIEncoding},
|
|
AVSSSEG6E8V & obj.AMask: {enc: sVIIEncoding},
|
|
AVSSSEG7E8V & obj.AMask: {enc: sVIIEncoding},
|
|
AVSSSEG8E8V & obj.AMask: {enc: sVIIEncoding},
|
|
AVSSSEG2E16V & obj.AMask: {enc: sVIIEncoding},
|
|
AVSSSEG3E16V & obj.AMask: {enc: sVIIEncoding},
|
|
AVSSSEG4E16V & obj.AMask: {enc: sVIIEncoding},
|
|
AVSSSEG5E16V & obj.AMask: {enc: sVIIEncoding},
|
|
AVSSSEG6E16V & obj.AMask: {enc: sVIIEncoding},
|
|
AVSSSEG7E16V & obj.AMask: {enc: sVIIEncoding},
|
|
AVSSSEG8E16V & obj.AMask: {enc: sVIIEncoding},
|
|
AVSSSEG2E32V & obj.AMask: {enc: sVIIEncoding},
|
|
AVSSSEG3E32V & obj.AMask: {enc: sVIIEncoding},
|
|
AVSSSEG4E32V & obj.AMask: {enc: sVIIEncoding},
|
|
AVSSSEG5E32V & obj.AMask: {enc: sVIIEncoding},
|
|
AVSSSEG6E32V & obj.AMask: {enc: sVIIEncoding},
|
|
AVSSSEG7E32V & obj.AMask: {enc: sVIIEncoding},
|
|
AVSSSEG8E32V & obj.AMask: {enc: sVIIEncoding},
|
|
AVSSSEG2E64V & obj.AMask: {enc: sVIIEncoding},
|
|
AVSSSEG3E64V & obj.AMask: {enc: sVIIEncoding},
|
|
AVSSSEG4E64V & obj.AMask: {enc: sVIIEncoding},
|
|
AVSSSEG5E64V & obj.AMask: {enc: sVIIEncoding},
|
|
AVSSSEG6E64V & obj.AMask: {enc: sVIIEncoding},
|
|
AVSSSEG7E64V & obj.AMask: {enc: sVIIEncoding},
|
|
AVSSSEG8E64V & obj.AMask: {enc: sVIIEncoding},
|
|
AVLOXSEG2EI8V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLOXSEG3EI8V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLOXSEG4EI8V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLOXSEG5EI8V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLOXSEG6EI8V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLOXSEG7EI8V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLOXSEG8EI8V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLOXSEG2EI16V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLOXSEG3EI16V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLOXSEG4EI16V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLOXSEG5EI16V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLOXSEG6EI16V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLOXSEG7EI16V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLOXSEG8EI16V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLOXSEG2EI32V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLOXSEG3EI32V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLOXSEG4EI32V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLOXSEG5EI32V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLOXSEG6EI32V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLOXSEG7EI32V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLOXSEG8EI32V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLOXSEG2EI64V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLOXSEG3EI64V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLOXSEG4EI64V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLOXSEG5EI64V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLOXSEG6EI64V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLOXSEG7EI64V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLOXSEG8EI64V & obj.AMask: {enc: iVIVEncoding},
|
|
AVSOXSEG2EI8V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSOXSEG3EI8V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSOXSEG4EI8V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSOXSEG5EI8V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSOXSEG6EI8V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSOXSEG7EI8V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSOXSEG8EI8V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSOXSEG2EI16V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSOXSEG3EI16V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSOXSEG4EI16V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSOXSEG5EI16V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSOXSEG6EI16V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSOXSEG7EI16V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSOXSEG8EI16V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSOXSEG2EI32V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSOXSEG3EI32V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSOXSEG4EI32V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSOXSEG5EI32V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSOXSEG6EI32V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSOXSEG7EI32V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSOXSEG8EI32V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSOXSEG2EI64V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSOXSEG3EI64V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSOXSEG4EI64V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSOXSEG5EI64V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSOXSEG6EI64V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSOXSEG7EI64V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSOXSEG8EI64V & obj.AMask: {enc: sVIVEncoding},
|
|
AVLUXSEG2EI8V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLUXSEG3EI8V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLUXSEG4EI8V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLUXSEG5EI8V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLUXSEG6EI8V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLUXSEG7EI8V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLUXSEG8EI8V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLUXSEG2EI16V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLUXSEG3EI16V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLUXSEG4EI16V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLUXSEG5EI16V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLUXSEG6EI16V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLUXSEG7EI16V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLUXSEG8EI16V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLUXSEG2EI32V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLUXSEG3EI32V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLUXSEG4EI32V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLUXSEG5EI32V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLUXSEG6EI32V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLUXSEG7EI32V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLUXSEG8EI32V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLUXSEG2EI64V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLUXSEG3EI64V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLUXSEG4EI64V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLUXSEG5EI64V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLUXSEG6EI64V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLUXSEG7EI64V & obj.AMask: {enc: iVIVEncoding},
|
|
AVLUXSEG8EI64V & obj.AMask: {enc: iVIVEncoding},
|
|
AVSUXSEG2EI8V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSUXSEG3EI8V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSUXSEG4EI8V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSUXSEG5EI8V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSUXSEG6EI8V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSUXSEG7EI8V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSUXSEG8EI8V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSUXSEG2EI16V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSUXSEG3EI16V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSUXSEG4EI16V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSUXSEG5EI16V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSUXSEG6EI16V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSUXSEG7EI16V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSUXSEG8EI16V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSUXSEG2EI32V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSUXSEG3EI32V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSUXSEG4EI32V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSUXSEG5EI32V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSUXSEG6EI32V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSUXSEG7EI32V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSUXSEG8EI32V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSUXSEG2EI64V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSUXSEG3EI64V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSUXSEG4EI64V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSUXSEG5EI64V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSUXSEG6EI64V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSUXSEG7EI64V & obj.AMask: {enc: sVIVEncoding},
|
|
AVSUXSEG8EI64V & obj.AMask: {enc: sVIVEncoding},
|
|
|
|
// 31.7.9: Vector Load/Store Whole Register Instructions
|
|
AVL1RE8V & obj.AMask: {enc: iVEncoding},
|
|
AVL1RE16V & obj.AMask: {enc: iVEncoding},
|
|
AVL1RE32V & obj.AMask: {enc: iVEncoding},
|
|
AVL1RE64V & obj.AMask: {enc: iVEncoding},
|
|
AVL2RE8V & obj.AMask: {enc: iVEncoding},
|
|
AVL2RE16V & obj.AMask: {enc: iVEncoding},
|
|
AVL2RE32V & obj.AMask: {enc: iVEncoding},
|
|
AVL2RE64V & obj.AMask: {enc: iVEncoding},
|
|
AVL4RE8V & obj.AMask: {enc: iVEncoding},
|
|
AVL4RE16V & obj.AMask: {enc: iVEncoding},
|
|
AVL4RE32V & obj.AMask: {enc: iVEncoding},
|
|
AVL4RE64V & obj.AMask: {enc: iVEncoding},
|
|
AVL8RE8V & obj.AMask: {enc: iVEncoding},
|
|
AVL8RE16V & obj.AMask: {enc: iVEncoding},
|
|
AVL8RE32V & obj.AMask: {enc: iVEncoding},
|
|
AVL8RE64V & obj.AMask: {enc: iVEncoding},
|
|
AVS1RV & obj.AMask: {enc: sVEncoding},
|
|
AVS2RV & obj.AMask: {enc: sVEncoding},
|
|
AVS4RV & obj.AMask: {enc: sVEncoding},
|
|
AVS8RV & obj.AMask: {enc: sVEncoding},
|
|
|
|
// 31.11.1: Vector Single-Width Integer Add and Subtract
|
|
AVADDVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVADDVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVADDVI & obj.AMask: {enc: rVViEncoding},
|
|
AVSUBVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVSUBVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVRSUBVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVRSUBVI & obj.AMask: {enc: rVViEncoding},
|
|
|
|
// 31.11.2: Vector Widening Integer Add/Subtract
|
|
AVWADDUVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVWADDUVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVWSUBUVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVWSUBUVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVWADDVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVWADDVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVWSUBVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVWSUBVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVWADDUWV & obj.AMask: {enc: rVVVEncoding},
|
|
AVWADDUWX & obj.AMask: {enc: rVIVEncoding},
|
|
AVWSUBUWV & obj.AMask: {enc: rVVVEncoding},
|
|
AVWSUBUWX & obj.AMask: {enc: rVIVEncoding},
|
|
AVWADDWV & obj.AMask: {enc: rVVVEncoding},
|
|
AVWADDWX & obj.AMask: {enc: rVIVEncoding},
|
|
AVWSUBWV & obj.AMask: {enc: rVVVEncoding},
|
|
AVWSUBWX & obj.AMask: {enc: rVIVEncoding},
|
|
|
|
// 31.11.3: Vector Integer Extension
|
|
AVZEXTVF2 & obj.AMask: {enc: rVVEncoding},
|
|
AVSEXTVF2 & obj.AMask: {enc: rVVEncoding},
|
|
AVZEXTVF4 & obj.AMask: {enc: rVVEncoding},
|
|
AVSEXTVF4 & obj.AMask: {enc: rVVEncoding},
|
|
AVZEXTVF8 & obj.AMask: {enc: rVVEncoding},
|
|
AVSEXTVF8 & obj.AMask: {enc: rVVEncoding},
|
|
|
|
// 31.11.4: Vector Integer Add-with-Carry / Subtract-with-Borrow Instructions
|
|
AVADCVVM & obj.AMask: {enc: rVVVEncoding},
|
|
AVADCVXM & obj.AMask: {enc: rVIVEncoding},
|
|
AVADCVIM & obj.AMask: {enc: rVViEncoding},
|
|
AVMADCVVM & obj.AMask: {enc: rVVVEncoding},
|
|
AVMADCVXM & obj.AMask: {enc: rVIVEncoding},
|
|
AVMADCVIM & obj.AMask: {enc: rVViEncoding},
|
|
AVMADCVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVMADCVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVMADCVI & obj.AMask: {enc: rVViEncoding},
|
|
AVSBCVVM & obj.AMask: {enc: rVVVEncoding},
|
|
AVSBCVXM & obj.AMask: {enc: rVIVEncoding},
|
|
AVMSBCVVM & obj.AMask: {enc: rVVVEncoding},
|
|
AVMSBCVXM & obj.AMask: {enc: rVIVEncoding},
|
|
AVMSBCVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVMSBCVX & obj.AMask: {enc: rVIVEncoding},
|
|
|
|
// 31.11.5: Vector Bitwise Logical Instructions
|
|
AVANDVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVANDVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVANDVI & obj.AMask: {enc: rVViEncoding},
|
|
AVORVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVORVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVORVI & obj.AMask: {enc: rVViEncoding},
|
|
AVXORVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVXORVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVXORVI & obj.AMask: {enc: rVViEncoding},
|
|
|
|
// 31.11.6: Vector Single-Width Shift Instructions
|
|
AVSLLVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVSLLVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVSLLVI & obj.AMask: {enc: rVVuEncoding},
|
|
AVSRLVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVSRLVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVSRLVI & obj.AMask: {enc: rVVuEncoding},
|
|
AVSRAVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVSRAVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVSRAVI & obj.AMask: {enc: rVVuEncoding},
|
|
|
|
// 31.11.7: Vector Narrowing Integer Right Shift Instructions
|
|
AVNSRLWV & obj.AMask: {enc: rVVVEncoding},
|
|
AVNSRLWX & obj.AMask: {enc: rVIVEncoding},
|
|
AVNSRLWI & obj.AMask: {enc: rVVuEncoding},
|
|
AVNSRAWV & obj.AMask: {enc: rVVVEncoding},
|
|
AVNSRAWX & obj.AMask: {enc: rVIVEncoding},
|
|
AVNSRAWI & obj.AMask: {enc: rVVuEncoding},
|
|
|
|
// 31.11.8: Vector Integer Compare Instructions
|
|
AVMSEQVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVMSEQVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVMSEQVI & obj.AMask: {enc: rVViEncoding},
|
|
AVMSNEVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVMSNEVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVMSNEVI & obj.AMask: {enc: rVViEncoding},
|
|
AVMSLTUVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVMSLTUVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVMSLTVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVMSLTVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVMSLEUVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVMSLEUVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVMSLEUVI & obj.AMask: {enc: rVViEncoding},
|
|
AVMSLEVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVMSLEVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVMSLEVI & obj.AMask: {enc: rVViEncoding},
|
|
AVMSGTUVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVMSGTUVI & obj.AMask: {enc: rVViEncoding},
|
|
AVMSGTVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVMSGTVI & obj.AMask: {enc: rVViEncoding},
|
|
|
|
// 31.11.9: Vector Integer Min/Max Instructions
|
|
AVMINUVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVMINUVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVMINVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVMINVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVMAXUVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVMAXUVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVMAXVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVMAXVX & obj.AMask: {enc: rVIVEncoding},
|
|
|
|
// 31.11.10: Vector Single-Width Integer Multiply Instructions
|
|
AVMULVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVMULVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVMULHVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVMULHVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVMULHUVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVMULHUVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVMULHSUVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVMULHSUVX & obj.AMask: {enc: rVIVEncoding},
|
|
|
|
// 31.11.11: Vector Integer Divide Instructions
|
|
AVDIVUVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVDIVUVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVDIVVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVDIVVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVREMUVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVREMUVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVREMVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVREMVX & obj.AMask: {enc: rVIVEncoding},
|
|
|
|
// 31.11.12: Vector Widening Integer Multiply Instructions
|
|
AVWMULVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVWMULVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVWMULUVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVWMULUVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVWMULSUVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVWMULSUVX & obj.AMask: {enc: rVIVEncoding},
|
|
|
|
// 31.11.13: Vector Single-Width Integer Multiply-Add Instructions
|
|
AVMACCVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVMACCVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVNMSACVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVNMSACVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVMADDVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVMADDVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVNMSUBVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVNMSUBVX & obj.AMask: {enc: rVIVEncoding},
|
|
|
|
// 31.11.14: Vector Widening Integer Multiply-Add Instructions
|
|
AVWMACCUVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVWMACCUVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVWMACCVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVWMACCVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVWMACCSUVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVWMACCSUVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVWMACCUSVX & obj.AMask: {enc: rVIVEncoding},
|
|
|
|
// 31.11.15: Vector Integer Merge Instructions
|
|
AVMERGEVVM & obj.AMask: {enc: rVVVEncoding},
|
|
AVMERGEVXM & obj.AMask: {enc: rVIVEncoding},
|
|
AVMERGEVIM & obj.AMask: {enc: rVViEncoding},
|
|
|
|
// 31.11.16: Vector Integer Move Instructions
|
|
AVMVVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVMVVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVMVVI & obj.AMask: {enc: rVViEncoding},
|
|
|
|
// 31.12.1: Vector Single-Width Saturating Add and Subtract
|
|
AVSADDUVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVSADDUVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVSADDUVI & obj.AMask: {enc: rVViEncoding},
|
|
AVSADDVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVSADDVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVSADDVI & obj.AMask: {enc: rVViEncoding},
|
|
AVSSUBUVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVSSUBUVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVSSUBVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVSSUBVX & obj.AMask: {enc: rVIVEncoding},
|
|
|
|
// 31.12.2: Vector Single-Width Averaging Add and Subtract
|
|
AVAADDUVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVAADDUVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVAADDVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVAADDVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVASUBUVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVASUBUVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVASUBVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVASUBVX & obj.AMask: {enc: rVIVEncoding},
|
|
|
|
// 31.12.3: Vector Single-Width Fractional Multiply with Rounding and Saturation
|
|
AVSMULVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVSMULVX & obj.AMask: {enc: rVIVEncoding},
|
|
|
|
// 31.12.4: Vector Single-Width Scaling Shift Instructions
|
|
AVSSRLVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVSSRLVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVSSRLVI & obj.AMask: {enc: rVVuEncoding},
|
|
AVSSRAVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVSSRAVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVSSRAVI & obj.AMask: {enc: rVVuEncoding},
|
|
|
|
// 31.12.5: Vector Narrowing Fixed-Point Clip Instructions
|
|
AVNCLIPUWV & obj.AMask: {enc: rVVVEncoding},
|
|
AVNCLIPUWX & obj.AMask: {enc: rVIVEncoding},
|
|
AVNCLIPUWI & obj.AMask: {enc: rVVuEncoding},
|
|
AVNCLIPWV & obj.AMask: {enc: rVVVEncoding},
|
|
AVNCLIPWX & obj.AMask: {enc: rVIVEncoding},
|
|
AVNCLIPWI & obj.AMask: {enc: rVVuEncoding},
|
|
|
|
// 31.13.2: Vector Single-Width Floating-Point Add/Subtract Instructions
|
|
AVFADDVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVFADDVF & obj.AMask: {enc: rVFVEncoding},
|
|
AVFSUBVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVFSUBVF & obj.AMask: {enc: rVFVEncoding},
|
|
AVFRSUBVF & obj.AMask: {enc: rVFVEncoding},
|
|
|
|
// 31.13.3: Vector Widening Floating-Point Add/Subtract Instructions
|
|
AVFWADDVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVFWADDVF & obj.AMask: {enc: rVFVEncoding},
|
|
AVFWSUBVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVFWSUBVF & obj.AMask: {enc: rVFVEncoding},
|
|
AVFWADDWV & obj.AMask: {enc: rVVVEncoding},
|
|
AVFWADDWF & obj.AMask: {enc: rVFVEncoding},
|
|
AVFWSUBWV & obj.AMask: {enc: rVVVEncoding},
|
|
AVFWSUBWF & obj.AMask: {enc: rVFVEncoding},
|
|
|
|
// 31.13.4: Vector Single-Width Floating-Point Multiply/Divide Instructions
|
|
AVFMULVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVFMULVF & obj.AMask: {enc: rVFVEncoding},
|
|
AVFDIVVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVFDIVVF & obj.AMask: {enc: rVFVEncoding},
|
|
AVFRDIVVF & obj.AMask: {enc: rVFVEncoding},
|
|
|
|
// 31.13.5: Vector Widening Floating-Point Multiply
|
|
AVFWMULVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVFWMULVF & obj.AMask: {enc: rVFVEncoding},
|
|
|
|
// 31.13.6: Vector Single-Width Floating-Point Fused Multiply-Add Instructions
|
|
AVFMACCVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVFMACCVF & obj.AMask: {enc: rVFVEncoding},
|
|
AVFNMACCVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVFNMACCVF & obj.AMask: {enc: rVFVEncoding},
|
|
AVFMSACVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVFMSACVF & obj.AMask: {enc: rVFVEncoding},
|
|
AVFNMSACVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVFNMSACVF & obj.AMask: {enc: rVFVEncoding},
|
|
AVFMADDVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVFMADDVF & obj.AMask: {enc: rVFVEncoding},
|
|
AVFNMADDVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVFNMADDVF & obj.AMask: {enc: rVFVEncoding},
|
|
AVFMSUBVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVFMSUBVF & obj.AMask: {enc: rVFVEncoding},
|
|
AVFNMSUBVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVFNMSUBVF & obj.AMask: {enc: rVFVEncoding},
|
|
|
|
// 31.13.7: Vector Widening Floating-Point Fused Multiply-Add Instructions
|
|
AVFWMACCVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVFWMACCVF & obj.AMask: {enc: rVFVEncoding},
|
|
AVFWNMACCVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVFWNMACCVF & obj.AMask: {enc: rVFVEncoding},
|
|
AVFWMSACVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVFWMSACVF & obj.AMask: {enc: rVFVEncoding},
|
|
AVFWNMSACVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVFWNMSACVF & obj.AMask: {enc: rVFVEncoding},
|
|
|
|
// 31.13.8: Vector Floating-Point Square-Root Instruction
|
|
AVFSQRTV & obj.AMask: {enc: rVVEncoding},
|
|
|
|
// 31.13.9: Vector Floating-Point Reciprocal Square-Root Estimate Instruction
|
|
AVFRSQRT7V & obj.AMask: {enc: rVVEncoding},
|
|
|
|
// 31.13.10: Vector Floating-Point Reciprocal Estimate Instruction
|
|
AVFREC7V & obj.AMask: {enc: rVVEncoding},
|
|
|
|
// 31.13.11: Vector Floating-Point MIN/MAX Instructions
|
|
AVFMINVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVFMINVF & obj.AMask: {enc: rVFVEncoding},
|
|
AVFMAXVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVFMAXVF & obj.AMask: {enc: rVFVEncoding},
|
|
|
|
// 31.13.12: Vector Floating-Point Sign-Injection Instructions
|
|
AVFSGNJVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVFSGNJVF & obj.AMask: {enc: rVFVEncoding},
|
|
AVFSGNJNVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVFSGNJNVF & obj.AMask: {enc: rVFVEncoding},
|
|
AVFSGNJXVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVFSGNJXVF & obj.AMask: {enc: rVFVEncoding},
|
|
|
|
// 31.13.13: Vector Floating-Point Compare Instructions
|
|
AVMFEQVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVMFEQVF & obj.AMask: {enc: rVFVEncoding},
|
|
AVMFNEVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVMFNEVF & obj.AMask: {enc: rVFVEncoding},
|
|
AVMFLTVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVMFLTVF & obj.AMask: {enc: rVFVEncoding},
|
|
AVMFLEVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVMFLEVF & obj.AMask: {enc: rVFVEncoding},
|
|
AVMFGTVF & obj.AMask: {enc: rVFVEncoding},
|
|
AVMFGEVF & obj.AMask: {enc: rVFVEncoding},
|
|
|
|
// 31.13.14: Vector Floating-Point Classify Instruction
|
|
AVFCLASSV & obj.AMask: {enc: rVVEncoding},
|
|
|
|
// 31.13.15: Vector Floating-Point Merge Instruction
|
|
AVFMERGEVFM & obj.AMask: {enc: rVFVEncoding},
|
|
|
|
// 31.13.16: Vector Floating-Point Move Instruction
|
|
AVFMVVF & obj.AMask: {enc: rVFVEncoding},
|
|
|
|
// 31.13.17: Single-Width Floating-Point/Integer Type-Convert Instructions
|
|
AVFCVTXUFV & obj.AMask: {enc: rVVEncoding},
|
|
AVFCVTXFV & obj.AMask: {enc: rVVEncoding},
|
|
AVFCVTRTZXUFV & obj.AMask: {enc: rVVEncoding},
|
|
AVFCVTRTZXFV & obj.AMask: {enc: rVVEncoding},
|
|
AVFCVTFXUV & obj.AMask: {enc: rVVEncoding},
|
|
AVFCVTFXV & obj.AMask: {enc: rVVEncoding},
|
|
|
|
// 31.13.18: Widening Floating-Point/Integer Type-Convert Instructions
|
|
AVFWCVTXUFV & obj.AMask: {enc: rVVEncoding},
|
|
AVFWCVTXFV & obj.AMask: {enc: rVVEncoding},
|
|
AVFWCVTRTZXUFV & obj.AMask: {enc: rVVEncoding},
|
|
AVFWCVTRTZXFV & obj.AMask: {enc: rVVEncoding},
|
|
AVFWCVTFXUV & obj.AMask: {enc: rVVEncoding},
|
|
AVFWCVTFXV & obj.AMask: {enc: rVVEncoding},
|
|
AVFWCVTFFV & obj.AMask: {enc: rVVEncoding},
|
|
|
|
// 31.13.19: Narrowing Floating-Point/Integer Type-Convert Instructions
|
|
AVFNCVTXUFW & obj.AMask: {enc: rVVEncoding},
|
|
AVFNCVTXFW & obj.AMask: {enc: rVVEncoding},
|
|
AVFNCVTRTZXUFW & obj.AMask: {enc: rVVEncoding},
|
|
AVFNCVTRTZXFW & obj.AMask: {enc: rVVEncoding},
|
|
AVFNCVTFXUW & obj.AMask: {enc: rVVEncoding},
|
|
AVFNCVTFXW & obj.AMask: {enc: rVVEncoding},
|
|
AVFNCVTFFW & obj.AMask: {enc: rVVEncoding},
|
|
AVFNCVTRODFFW & obj.AMask: {enc: rVVEncoding},
|
|
|
|
// 31.14.1: Vector Single-Width Integer Reduction Instructions
|
|
AVREDSUMVS & obj.AMask: {enc: rVVVEncoding},
|
|
AVREDMAXUVS & obj.AMask: {enc: rVVVEncoding},
|
|
AVREDMAXVS & obj.AMask: {enc: rVVVEncoding},
|
|
AVREDMINUVS & obj.AMask: {enc: rVVVEncoding},
|
|
AVREDMINVS & obj.AMask: {enc: rVVVEncoding},
|
|
AVREDANDVS & obj.AMask: {enc: rVVVEncoding},
|
|
AVREDORVS & obj.AMask: {enc: rVVVEncoding},
|
|
AVREDXORVS & obj.AMask: {enc: rVVVEncoding},
|
|
|
|
// 31.14.2: Vector Widening Integer Reduction Instructions
|
|
AVWREDSUMUVS & obj.AMask: {enc: rVVVEncoding},
|
|
AVWREDSUMVS & obj.AMask: {enc: rVVVEncoding},
|
|
|
|
// 31.14.3: Vector Single-Width Floating-Point Reduction Instructions
|
|
AVFREDOSUMVS & obj.AMask: {enc: rVVVEncoding},
|
|
AVFREDUSUMVS & obj.AMask: {enc: rVVVEncoding},
|
|
AVFREDMAXVS & obj.AMask: {enc: rVVVEncoding},
|
|
AVFREDMINVS & obj.AMask: {enc: rVVVEncoding},
|
|
|
|
// 31.14.4: Vector Widening Floating-Point Reduction Instructions
|
|
AVFWREDOSUMVS & obj.AMask: {enc: rVVVEncoding},
|
|
AVFWREDUSUMVS & obj.AMask: {enc: rVVVEncoding},
|
|
|
|
// 31.15: Vector Mask Instructions
|
|
AVMANDMM & obj.AMask: {enc: rVVVEncoding},
|
|
AVMNANDMM & obj.AMask: {enc: rVVVEncoding},
|
|
AVMANDNMM & obj.AMask: {enc: rVVVEncoding},
|
|
AVMXORMM & obj.AMask: {enc: rVVVEncoding},
|
|
AVMORMM & obj.AMask: {enc: rVVVEncoding},
|
|
AVMNORMM & obj.AMask: {enc: rVVVEncoding},
|
|
AVMORNMM & obj.AMask: {enc: rVVVEncoding},
|
|
AVMXNORMM & obj.AMask: {enc: rVVVEncoding},
|
|
AVCPOPM & obj.AMask: {enc: rVIEncoding},
|
|
AVFIRSTM & obj.AMask: {enc: rVIEncoding},
|
|
AVMSBFM & obj.AMask: {enc: rVVEncoding},
|
|
AVMSIFM & obj.AMask: {enc: rVVEncoding},
|
|
AVMSOFM & obj.AMask: {enc: rVVEncoding},
|
|
AVIOTAM & obj.AMask: {enc: rVVEncoding},
|
|
AVIDV & obj.AMask: {enc: rVVEncoding},
|
|
|
|
// 31.16.1: Integer Scalar Move Instructions
|
|
AVMVXS & obj.AMask: {enc: rVIEncoding},
|
|
AVMVSX & obj.AMask: {enc: rIVEncoding},
|
|
|
|
// 31.16.2: Floating-Point Scalar Move Instructions
|
|
AVFMVFS & obj.AMask: {enc: rVFEncoding},
|
|
AVFMVSF & obj.AMask: {enc: rFVEncoding},
|
|
|
|
// 31.16.3: Vector Slide Instructions
|
|
AVSLIDEUPVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVSLIDEUPVI & obj.AMask: {enc: rVVuEncoding},
|
|
AVSLIDEDOWNVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVSLIDEDOWNVI & obj.AMask: {enc: rVVuEncoding},
|
|
AVSLIDE1UPVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVFSLIDE1UPVF & obj.AMask: {enc: rVFVEncoding},
|
|
AVSLIDE1DOWNVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVFSLIDE1DOWNVF & obj.AMask: {enc: rVFVEncoding},
|
|
|
|
// 31.16.4: Vector Register Gather Instructions
|
|
AVRGATHERVV & obj.AMask: {enc: rVVVEncoding},
|
|
AVRGATHEREI16VV & obj.AMask: {enc: rVVVEncoding},
|
|
AVRGATHERVX & obj.AMask: {enc: rVIVEncoding},
|
|
AVRGATHERVI & obj.AMask: {enc: rVVuEncoding},
|
|
|
|
// 31.16.5: Vector Compress Instruction
|
|
AVCOMPRESSVM & obj.AMask: {enc: rVVVEncoding},
|
|
|
|
// 31.16.6: Whole Vector Register Move
|
|
AVMV1RV & obj.AMask: {enc: rVVEncoding},
|
|
AVMV2RV & obj.AMask: {enc: rVVEncoding},
|
|
AVMV4RV & obj.AMask: {enc: rVVEncoding},
|
|
AVMV8RV & obj.AMask: {enc: rVVEncoding},
|
|
|
|
//
|
|
// Privileged ISA
|
|
//
|
|
|
|
// 3.3.1: Environment Call and Breakpoint
|
|
AECALL & obj.AMask: {enc: iIIEncoding},
|
|
AEBREAK & obj.AMask: {enc: iIIEncoding},
|
|
|
|
// Escape hatch
|
|
AWORD & obj.AMask: {enc: rawEncoding},
|
|
|
|
// Pseudo-operations
|
|
obj.AFUNCDATA: {enc: pseudoOpEncoding},
|
|
obj.APCDATA: {enc: pseudoOpEncoding},
|
|
obj.ATEXT: {enc: pseudoOpEncoding},
|
|
obj.ANOP: {enc: pseudoOpEncoding},
|
|
obj.APCALIGN: {enc: pseudoOpEncoding},
|
|
}
|
|
|
|
// instructionDataForAs returns the instruction data for an obj.As.
|
|
func instructionDataForAs(as obj.As) (*instructionData, error) {
|
|
if base := as &^ obj.AMask; base != obj.ABaseRISCV && base != 0 {
|
|
return nil, fmt.Errorf("%v is not a RISC-V instruction", as)
|
|
}
|
|
asi := as & obj.AMask
|
|
if int(asi) >= len(instructions) {
|
|
return nil, fmt.Errorf("bad RISC-V instruction %v", as)
|
|
}
|
|
return &instructions[asi], nil
|
|
}
|
|
|
|
// encodingForAs returns the encoding for an obj.As.
|
|
func encodingForAs(as obj.As) (*encoding, error) {
|
|
insData, err := instructionDataForAs(as)
|
|
if err != nil {
|
|
return &badEncoding, err
|
|
}
|
|
if insData.enc.validate == nil {
|
|
return &badEncoding, fmt.Errorf("no encoding for instruction %s", as)
|
|
}
|
|
return &insData.enc, nil
|
|
}
|
|
|
|
// splitShiftConst attempts to split a constant into a signed 12 bit or
|
|
// 32 bit integer, with corresponding logical right shift and/or left shift.
|
|
func splitShiftConst(v int64) (imm int64, lsh int, rsh int, ok bool) {
|
|
// See if we can reconstruct this value from a signed 32 bit integer.
|
|
lsh = bits.TrailingZeros64(uint64(v))
|
|
c := v >> lsh
|
|
if int64(int32(c)) == c {
|
|
return c, lsh, 0, true
|
|
}
|
|
|
|
// See if we can reconstruct this value from a small negative constant.
|
|
rsh = bits.LeadingZeros64(uint64(v))
|
|
ones := bits.OnesCount64((uint64(v) >> lsh) >> 11)
|
|
c = signExtend(1<<11|((v>>lsh)&0x7ff), 12)
|
|
if rsh+ones+lsh+11 == 64 {
|
|
if lsh > 0 || c != -1 {
|
|
lsh += rsh
|
|
}
|
|
return c, lsh, rsh, true
|
|
}
|
|
|
|
return 0, 0, 0, false
|
|
}
|
|
|
|
// isShiftConst indicates whether a constant can be represented as a signed
|
|
// 32 bit integer that is left shifted.
|
|
func isShiftConst(v int64) bool {
|
|
_, lsh, rsh, ok := splitShiftConst(v)
|
|
return ok && (lsh > 0 || rsh > 0)
|
|
}
|
|
|
|
type instruction struct {
|
|
p *obj.Prog // Prog that instruction is for
|
|
as obj.As // Assembler opcode
|
|
rd uint32 // Destination register
|
|
rs1 uint32 // Source register 1
|
|
rs2 uint32 // Source register 2
|
|
rs3 uint32 // Source register 3
|
|
imm int64 // Immediate
|
|
funct3 uint32 // Function 3
|
|
funct7 uint32 // Function 7 (or Function 2)
|
|
}
|
|
|
|
func (ins *instruction) String() string {
|
|
if ins.p == nil {
|
|
return ins.as.String()
|
|
}
|
|
var suffix string
|
|
if ins.p.As != ins.as {
|
|
suffix = fmt.Sprintf(" (%v)", ins.as)
|
|
}
|
|
return fmt.Sprintf("%v%v", ins.p, suffix)
|
|
}
|
|
|
|
func (ins *instruction) encode() (uint32, error) {
|
|
enc, err := encodingForAs(ins.as)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
if enc.length <= 0 {
|
|
return 0, fmt.Errorf("%v: encoding called for a pseudo instruction", ins.as)
|
|
}
|
|
return enc.encode(ins), nil
|
|
}
|
|
|
|
func (ins *instruction) length() int {
|
|
enc, err := encodingForAs(ins.as)
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
return enc.length
|
|
}
|
|
|
|
func (ins *instruction) validate(ctxt *obj.Link) {
|
|
enc, err := encodingForAs(ins.as)
|
|
if err != nil {
|
|
ctxt.Diag("%v", err)
|
|
return
|
|
}
|
|
enc.validate(ctxt, ins)
|
|
}
|
|
|
|
func (ins *instruction) usesRegTmp() bool {
|
|
return ins.rd == REG_TMP || ins.rs1 == REG_TMP || ins.rs2 == REG_TMP
|
|
}
|
|
|
|
func (ins *instruction) compress() {
|
|
switch ins.as {
|
|
case ALW:
|
|
if ins.rd != REG_X0 && ins.rs1 == REG_SP && isScaledImmU(ins.imm, 8, 4) {
|
|
ins.as, ins.rs1, ins.rs2 = ACLWSP, obj.REG_NONE, ins.rs1
|
|
} else if isIntPrimeReg(ins.rd) && isIntPrimeReg(ins.rs1) && isScaledImmU(ins.imm, 7, 4) {
|
|
ins.as = ACLW
|
|
}
|
|
|
|
case ALD:
|
|
if ins.rs1 == REG_SP && ins.rd != REG_X0 && isScaledImmU(ins.imm, 9, 8) {
|
|
ins.as, ins.rs1, ins.rs2 = ACLDSP, obj.REG_NONE, ins.rs1
|
|
} else if isIntPrimeReg(ins.rd) && isIntPrimeReg(ins.rs1) && isScaledImmU(ins.imm, 8, 8) {
|
|
ins.as = ACLD
|
|
}
|
|
|
|
case AFLD:
|
|
if ins.rs1 == REG_SP && isScaledImmU(ins.imm, 9, 8) {
|
|
ins.as, ins.rs1, ins.rs2 = ACFLDSP, obj.REG_NONE, ins.rs1
|
|
} else if isFloatPrimeReg(ins.rd) && isIntPrimeReg(ins.rs1) && isScaledImmU(ins.imm, 8, 8) {
|
|
ins.as = ACFLD
|
|
}
|
|
|
|
case ASW:
|
|
if ins.rd == REG_SP && isScaledImmU(ins.imm, 8, 4) {
|
|
ins.as, ins.rs1, ins.rs2 = ACSWSP, obj.REG_NONE, ins.rs1
|
|
} else if isIntPrimeReg(ins.rd) && isIntPrimeReg(ins.rs1) && isScaledImmU(ins.imm, 7, 4) {
|
|
ins.as, ins.rd, ins.rs1, ins.rs2 = ACSW, obj.REG_NONE, ins.rd, ins.rs1
|
|
}
|
|
|
|
case ASD:
|
|
if ins.rd == REG_SP && isScaledImmU(ins.imm, 9, 8) {
|
|
ins.as, ins.rs1, ins.rs2 = ACSDSP, obj.REG_NONE, ins.rs1
|
|
} else if isIntPrimeReg(ins.rd) && isIntPrimeReg(ins.rs1) && isScaledImmU(ins.imm, 8, 8) {
|
|
ins.as, ins.rd, ins.rs1, ins.rs2 = ACSD, obj.REG_NONE, ins.rd, ins.rs1
|
|
}
|
|
|
|
case AFSD:
|
|
if ins.rd == REG_SP && isScaledImmU(ins.imm, 9, 8) {
|
|
ins.as, ins.rs1, ins.rs2 = ACFSDSP, obj.REG_NONE, ins.rs1
|
|
} else if isIntPrimeReg(ins.rd) && isFloatPrimeReg(ins.rs1) && isScaledImmU(ins.imm, 8, 8) {
|
|
ins.as, ins.rd, ins.rs1, ins.rs2 = ACFSD, obj.REG_NONE, ins.rd, ins.rs1
|
|
}
|
|
|
|
case AADDI:
|
|
if ins.rd == REG_SP && ins.rs1 == REG_SP && ins.imm != 0 && isScaledImmI(ins.imm, 10, 16) {
|
|
ins.as = ACADDI16SP
|
|
} else if ins.rd != REG_X0 && ins.rd == ins.rs1 && ins.imm != 0 && immIFits(ins.imm, 6) == nil {
|
|
ins.as = ACADDI
|
|
} else if isIntPrimeReg(ins.rd) && ins.rs1 == REG_SP && ins.imm != 0 && isScaledImmU(ins.imm, 10, 4) {
|
|
ins.as = ACADDI4SPN
|
|
} else if ins.rd != REG_X0 && ins.rs1 == REG_X0 && immIFits(ins.imm, 6) == nil {
|
|
ins.as, ins.rs1 = ACLI, obj.REG_NONE
|
|
} else if ins.rd != REG_X0 && ins.rs1 != REG_X0 && ins.imm == 0 {
|
|
ins.as, ins.rs1, ins.rs2 = ACMV, obj.REG_NONE, ins.rs1
|
|
} else if ins.rd == REG_X0 && ins.rs1 == REG_X0 && ins.imm == 0 {
|
|
ins.as, ins.rs1 = ACNOP, ins.rd
|
|
}
|
|
|
|
case AADDIW:
|
|
if ins.rd == ins.rs1 && immIFits(ins.imm, 6) == nil {
|
|
ins.as = ACADDIW
|
|
}
|
|
|
|
case ALUI:
|
|
if ins.rd != REG_X0 && ins.rd != REG_SP && ins.imm != 0 && immIFits(ins.imm, 6) == nil {
|
|
ins.as = ACLUI
|
|
}
|
|
|
|
case ASLLI:
|
|
if ins.rd != REG_X0 && ins.rd == ins.rs1 && ins.imm != 0 {
|
|
ins.as = ACSLLI
|
|
}
|
|
|
|
case ASRLI:
|
|
if isIntPrimeReg(ins.rd) && ins.rd == ins.rs1 && ins.imm != 0 {
|
|
ins.as = ACSRLI
|
|
}
|
|
|
|
case ASRAI:
|
|
if isIntPrimeReg(ins.rd) && ins.rd == ins.rs1 && ins.imm != 0 {
|
|
ins.as = ACSRAI
|
|
}
|
|
|
|
case AANDI:
|
|
if isIntPrimeReg(ins.rd) && ins.rd == ins.rs1 && immIFits(ins.imm, 6) == nil {
|
|
ins.as = ACANDI
|
|
}
|
|
|
|
case AADD:
|
|
if ins.rd != REG_X0 && ins.rd == ins.rs1 && ins.rs2 != REG_X0 {
|
|
ins.as = ACADD
|
|
} else if ins.rd != REG_X0 && ins.rd == ins.rs2 && ins.rs1 != REG_X0 {
|
|
ins.as, ins.rs1, ins.rs2 = ACADD, ins.rs2, ins.rs1
|
|
} else if ins.rd != REG_X0 && ins.rs1 == REG_X0 && ins.rs2 != REG_X0 {
|
|
ins.as = ACMV
|
|
}
|
|
|
|
case AADDW:
|
|
if isIntPrimeReg(ins.rd) && ins.rd == ins.rs1 && isIntPrimeReg(ins.rs2) {
|
|
ins.as = ACADDW
|
|
} else if isIntPrimeReg(ins.rd) && isIntPrimeReg(ins.rs1) && ins.rd == ins.rs2 {
|
|
ins.as, ins.rs1, ins.rs2 = ACADDW, ins.rs2, ins.rs1
|
|
}
|
|
|
|
case ASUB:
|
|
if isIntPrimeReg(ins.rd) && ins.rd == ins.rs1 && isIntPrimeReg(ins.rs2) {
|
|
ins.as = ACSUB
|
|
}
|
|
|
|
case ASUBW:
|
|
if isIntPrimeReg(ins.rd) && ins.rd == ins.rs1 && isIntPrimeReg(ins.rs2) {
|
|
ins.as = ACSUBW
|
|
}
|
|
|
|
case AAND:
|
|
if isIntPrimeReg(ins.rd) && ins.rd == ins.rs1 && isIntPrimeReg(ins.rs2) {
|
|
ins.as = ACAND
|
|
} else if isIntPrimeReg(ins.rd) && isIntPrimeReg(ins.rs1) && ins.rd == ins.rs2 {
|
|
ins.as, ins.rs1, ins.rs2 = ACAND, ins.rs2, ins.rs1
|
|
}
|
|
|
|
case AOR:
|
|
if isIntPrimeReg(ins.rd) && ins.rd == ins.rs1 && isIntPrimeReg(ins.rs2) {
|
|
ins.as = ACOR
|
|
} else if isIntPrimeReg(ins.rd) && isIntPrimeReg(ins.rs1) && ins.rd == ins.rs2 {
|
|
ins.as, ins.rs1, ins.rs2 = ACOR, ins.rs2, ins.rs1
|
|
}
|
|
|
|
case AXOR:
|
|
if isIntPrimeReg(ins.rd) && ins.rd == ins.rs1 && isIntPrimeReg(ins.rs2) {
|
|
ins.as = ACXOR
|
|
} else if isIntPrimeReg(ins.rd) && isIntPrimeReg(ins.rs1) && ins.rd == ins.rs2 {
|
|
ins.as, ins.rs1, ins.rs2 = ACXOR, ins.rs2, ins.rs1
|
|
}
|
|
|
|
case AEBREAK:
|
|
ins.as, ins.rd, ins.rs1 = ACEBREAK, obj.REG_NONE, obj.REG_NONE
|
|
}
|
|
}
|
|
|
|
// instructionForProg returns the default *obj.Prog to instruction mapping.
|
|
func instructionForProg(p *obj.Prog) *instruction {
|
|
ins := &instruction{
|
|
as: p.As,
|
|
rd: uint32(p.To.Reg),
|
|
rs1: uint32(p.Reg),
|
|
rs2: uint32(p.From.Reg),
|
|
imm: p.From.Offset,
|
|
}
|
|
if len(p.RestArgs) == 1 {
|
|
ins.rs3 = uint32(p.RestArgs[0].Reg)
|
|
}
|
|
return ins
|
|
}
|
|
|
|
// instructionsForOpImmediate returns the machine instructions for an immediate
|
|
// operand. The instruction is specified by as and the source register is
|
|
// specified by rs, instead of the obj.Prog.
|
|
func instructionsForOpImmediate(p *obj.Prog, as obj.As, rs int16) []*instruction {
|
|
// <opi> $imm, REG, TO
|
|
ins := instructionForProg(p)
|
|
ins.as, ins.rs1, ins.rs2 = as, uint32(rs), obj.REG_NONE
|
|
|
|
low, high, err := Split32BitImmediate(ins.imm)
|
|
if err != nil {
|
|
p.Ctxt.Diag("%v: constant %d too large: %v", p, ins.imm, err)
|
|
return nil
|
|
}
|
|
if high == 0 {
|
|
return []*instruction{ins}
|
|
}
|
|
|
|
// Split into two additions, if possible.
|
|
// Do not split SP-writing instructions, as otherwise the recorded SP delta may be wrong.
|
|
if p.Spadj == 0 && ins.as == AADDI && ins.imm >= -(1<<12) && ins.imm < 1<<12-1 {
|
|
imm0 := ins.imm / 2
|
|
imm1 := ins.imm - imm0
|
|
|
|
// ADDI $(imm/2), REG, TO
|
|
// ADDI $(imm-imm/2), TO, TO
|
|
ins.imm = imm0
|
|
insADDI := &instruction{as: AADDI, rd: ins.rd, rs1: ins.rd, imm: imm1}
|
|
return []*instruction{ins, insADDI}
|
|
}
|
|
|
|
// LUI $high, TMP
|
|
// ADDIW $low, TMP, TMP
|
|
// <op> TMP, REG, TO
|
|
insLUI := &instruction{as: ALUI, rd: REG_TMP, imm: high}
|
|
insADDIW := &instruction{as: AADDIW, rd: REG_TMP, rs1: REG_TMP, imm: low}
|
|
switch ins.as {
|
|
case AADDI:
|
|
ins.as = AADD
|
|
case AANDI:
|
|
ins.as = AAND
|
|
case AORI:
|
|
ins.as = AOR
|
|
case AXORI:
|
|
ins.as = AXOR
|
|
default:
|
|
p.Ctxt.Diag("unsupported immediate instruction %v for splitting", p)
|
|
return nil
|
|
}
|
|
ins.rs2 = REG_TMP
|
|
if low == 0 {
|
|
return []*instruction{insLUI, ins}
|
|
}
|
|
return []*instruction{insLUI, insADDIW, ins}
|
|
}
|
|
|
|
// instructionsForLoad returns the machine instructions for a load. The load
|
|
// instruction is specified by as and the base/source register is specified
|
|
// by rs, instead of the obj.Prog.
|
|
func instructionsForLoad(p *obj.Prog, as obj.As, rs int16) []*instruction {
|
|
if p.From.Type != obj.TYPE_MEM {
|
|
p.Ctxt.Diag("%v requires memory for source", p)
|
|
return nil
|
|
}
|
|
|
|
switch as {
|
|
case ALD, ALB, ALH, ALW, ALBU, ALHU, ALWU, AFLW, AFLD:
|
|
default:
|
|
p.Ctxt.Diag("%v: unknown load instruction %v", p, as)
|
|
return nil
|
|
}
|
|
|
|
// <load> $imm, REG, TO (load $imm+(REG), TO)
|
|
ins := instructionForProg(p)
|
|
ins.as, ins.rs1, ins.rs2 = as, uint32(rs), obj.REG_NONE
|
|
ins.imm = p.From.Offset
|
|
|
|
low, high, err := Split32BitImmediate(ins.imm)
|
|
if err != nil {
|
|
p.Ctxt.Diag("%v: constant %d too large", p, ins.imm)
|
|
return nil
|
|
}
|
|
if high == 0 {
|
|
return []*instruction{ins}
|
|
}
|
|
|
|
// LUI $high, TMP
|
|
// ADD TMP, REG, TMP
|
|
// <load> $low, TMP, TO
|
|
insLUI := &instruction{as: ALUI, rd: REG_TMP, imm: high}
|
|
insADD := &instruction{as: AADD, rd: REG_TMP, rs1: REG_TMP, rs2: ins.rs1}
|
|
ins.rs1, ins.imm = REG_TMP, low
|
|
|
|
return []*instruction{insLUI, insADD, ins}
|
|
}
|
|
|
|
// instructionsForStore returns the machine instructions for a store. The store
|
|
// instruction is specified by as and the target/source register is specified
|
|
// by rd, instead of the obj.Prog.
|
|
func instructionsForStore(p *obj.Prog, as obj.As, rd int16) []*instruction {
|
|
if p.To.Type != obj.TYPE_MEM {
|
|
p.Ctxt.Diag("%v requires memory for destination", p)
|
|
return nil
|
|
}
|
|
|
|
switch as {
|
|
case ASW, ASH, ASB, ASD, AFSW, AFSD:
|
|
default:
|
|
p.Ctxt.Diag("%v: unknown store instruction %v", p, as)
|
|
return nil
|
|
}
|
|
|
|
// <store> $imm, REG, TO (store $imm+(TO), REG)
|
|
ins := instructionForProg(p)
|
|
ins.as, ins.rd, ins.rs1, ins.rs2 = as, uint32(rd), uint32(p.From.Reg), obj.REG_NONE
|
|
ins.imm = p.To.Offset
|
|
|
|
low, high, err := Split32BitImmediate(ins.imm)
|
|
if err != nil {
|
|
p.Ctxt.Diag("%v: constant %d too large", p, ins.imm)
|
|
return nil
|
|
}
|
|
if high == 0 {
|
|
return []*instruction{ins}
|
|
}
|
|
|
|
// LUI $high, TMP
|
|
// ADD TMP, TO, TMP
|
|
// <store> $low, REG, TMP
|
|
insLUI := &instruction{as: ALUI, rd: REG_TMP, imm: high}
|
|
insADD := &instruction{as: AADD, rd: REG_TMP, rs1: REG_TMP, rs2: ins.rd}
|
|
ins.rd, ins.imm = REG_TMP, low
|
|
|
|
return []*instruction{insLUI, insADD, ins}
|
|
}
|
|
|
|
func instructionsForTLS(p *obj.Prog, ins *instruction) []*instruction {
|
|
insAddTP := &instruction{as: AADD, rd: REG_TMP, rs1: REG_TMP, rs2: REG_TP}
|
|
|
|
var inss []*instruction
|
|
if p.Ctxt.Flag_shared {
|
|
// TLS initial-exec mode - load TLS offset from GOT, add the thread pointer
|
|
// register, then load from or store to the resulting memory location.
|
|
insAUIPC := &instruction{as: AAUIPC, rd: REG_TMP}
|
|
insLoadTLSOffset := &instruction{as: ALD, rd: REG_TMP, rs1: REG_TMP}
|
|
inss = []*instruction{insAUIPC, insLoadTLSOffset, insAddTP, ins}
|
|
} else {
|
|
// TLS local-exec mode - load upper TLS offset, add the lower TLS offset,
|
|
// add the thread pointer register, then load from or store to the resulting
|
|
// memory location. Note that this differs from the suggested three
|
|
// instruction sequence, as the Go linker does not currently have an
|
|
// easy way to handle relocation across 12 bytes of machine code.
|
|
insLUI := &instruction{as: ALUI, rd: REG_TMP}
|
|
insADDIW := &instruction{as: AADDIW, rd: REG_TMP, rs1: REG_TMP}
|
|
inss = []*instruction{insLUI, insADDIW, insAddTP, ins}
|
|
}
|
|
return inss
|
|
}
|
|
|
|
func instructionsForTLSLoad(p *obj.Prog) []*instruction {
|
|
if p.From.Sym.Type != objabi.STLSBSS {
|
|
p.Ctxt.Diag("%v: %v is not a TLS symbol", p, p.From.Sym)
|
|
return nil
|
|
}
|
|
|
|
ins := instructionForProg(p)
|
|
ins.as, ins.rs1, ins.rs2, ins.imm = movToLoad(p.As), REG_TMP, obj.REG_NONE, 0
|
|
|
|
return instructionsForTLS(p, ins)
|
|
}
|
|
|
|
func instructionsForTLSStore(p *obj.Prog) []*instruction {
|
|
if p.To.Sym.Type != objabi.STLSBSS {
|
|
p.Ctxt.Diag("%v: %v is not a TLS symbol", p, p.To.Sym)
|
|
return nil
|
|
}
|
|
|
|
ins := instructionForProg(p)
|
|
ins.as, ins.rd, ins.rs1, ins.rs2, ins.imm = movToStore(p.As), REG_TMP, uint32(p.From.Reg), obj.REG_NONE, 0
|
|
|
|
return instructionsForTLS(p, ins)
|
|
}
|
|
|
|
// instructionsForMOV returns the machine instructions for an *obj.Prog that
|
|
// uses a MOV pseudo-instruction.
|
|
func instructionsForMOV(p *obj.Prog) []*instruction {
|
|
ins := instructionForProg(p)
|
|
inss := []*instruction{ins}
|
|
|
|
if p.Reg != 0 {
|
|
p.Ctxt.Diag("%v: illegal MOV instruction", p)
|
|
return nil
|
|
}
|
|
|
|
switch {
|
|
case p.From.Type == obj.TYPE_CONST && p.To.Type == obj.TYPE_REG:
|
|
// Handle constant to register moves.
|
|
if p.As != AMOV {
|
|
p.Ctxt.Diag("%v: unsupported constant load", p)
|
|
return nil
|
|
}
|
|
|
|
// For constants larger than 32 bits in size that have trailing zeros,
|
|
// use the value with the trailing zeros removed and then use a SLLI
|
|
// instruction to restore the original constant.
|
|
//
|
|
// For example:
|
|
// MOV $0x8000000000000000, X10
|
|
// becomes
|
|
// MOV $1, X10
|
|
// SLLI $63, X10, X10
|
|
//
|
|
// Similarly, we can construct large constants that have a consecutive
|
|
// sequence of ones from a small negative constant, with a right and/or
|
|
// left shift.
|
|
//
|
|
// For example:
|
|
// MOV $0x000fffffffffffda, X10
|
|
// becomes
|
|
// MOV $-19, X10
|
|
// SLLI $13, X10
|
|
// SRLI $12, X10
|
|
//
|
|
var insSLLI, insSRLI *instruction
|
|
if err := immIFits(ins.imm, 32); err != nil {
|
|
if c, lsh, rsh, ok := splitShiftConst(ins.imm); ok {
|
|
ins.imm = c
|
|
if lsh > 0 {
|
|
insSLLI = &instruction{as: ASLLI, rd: ins.rd, rs1: ins.rd, imm: int64(lsh)}
|
|
}
|
|
if rsh > 0 {
|
|
insSRLI = &instruction{as: ASRLI, rd: ins.rd, rs1: ins.rd, imm: int64(rsh)}
|
|
}
|
|
}
|
|
}
|
|
|
|
low, high, err := Split32BitImmediate(ins.imm)
|
|
if err != nil {
|
|
p.Ctxt.Diag("%v: constant %d too large: %v", p, ins.imm, err)
|
|
return nil
|
|
}
|
|
|
|
// MOV $c, R -> ADD $c, ZERO, R
|
|
ins.as, ins.rs1, ins.rs2, ins.imm = AADDI, REG_ZERO, obj.REG_NONE, low
|
|
|
|
// LUI is only necessary if the constant does not fit in 12 bits.
|
|
if high != 0 {
|
|
// LUI top20bits(c), R
|
|
// ADD bottom12bits(c), R, R
|
|
insLUI := &instruction{as: ALUI, rd: ins.rd, imm: high}
|
|
inss = []*instruction{insLUI}
|
|
if low != 0 {
|
|
ins.as, ins.rs1 = AADDIW, ins.rd
|
|
inss = append(inss, ins)
|
|
}
|
|
}
|
|
if insSLLI != nil {
|
|
inss = append(inss, insSLLI)
|
|
}
|
|
if insSRLI != nil {
|
|
inss = append(inss, insSRLI)
|
|
}
|
|
|
|
case p.From.Type == obj.TYPE_CONST && p.To.Type != obj.TYPE_REG:
|
|
p.Ctxt.Diag("%v: constant load must target register", p)
|
|
return nil
|
|
|
|
case p.From.Type == obj.TYPE_REG && p.To.Type == obj.TYPE_REG:
|
|
// Handle register to register moves.
|
|
switch p.As {
|
|
case AMOV:
|
|
// MOV Ra, Rb -> ADDI $0, Ra, Rb
|
|
ins.as, ins.rs1, ins.rs2, ins.imm = AADDI, uint32(p.From.Reg), obj.REG_NONE, 0
|
|
case AMOVW:
|
|
// MOVW Ra, Rb -> ADDIW $0, Ra, Rb
|
|
ins.as, ins.rs1, ins.rs2, ins.imm = AADDIW, uint32(p.From.Reg), obj.REG_NONE, 0
|
|
case AMOVBU:
|
|
// MOVBU Ra, Rb -> ANDI $255, Ra, Rb
|
|
ins.as, ins.rs1, ins.rs2, ins.imm = AANDI, uint32(p.From.Reg), obj.REG_NONE, 255
|
|
case AMOVF:
|
|
// MOVF Ra, Rb -> FSGNJS Ra, Ra, Rb
|
|
// or -> FMVWX Ra, Rb
|
|
// or -> FMVXW Ra, Rb
|
|
if ins.rs2 >= REG_X0 && ins.rs2 <= REG_X31 && ins.rd >= REG_F0 && ins.rd <= REG_F31 {
|
|
ins.as = AFMVWX
|
|
} else if ins.rs2 >= REG_F0 && ins.rs2 <= REG_F31 && ins.rd >= REG_X0 && ins.rd <= REG_X31 {
|
|
ins.as = AFMVXW
|
|
} else {
|
|
ins.as, ins.rs1 = AFSGNJS, uint32(p.From.Reg)
|
|
}
|
|
case AMOVD:
|
|
// MOVD Ra, Rb -> FSGNJD Ra, Ra, Rb
|
|
// or -> FMVDX Ra, Rb
|
|
// or -> FMVXD Ra, Rb
|
|
if ins.rs2 >= REG_X0 && ins.rs2 <= REG_X31 && ins.rd >= REG_F0 && ins.rd <= REG_F31 {
|
|
ins.as = AFMVDX
|
|
} else if ins.rs2 >= REG_F0 && ins.rs2 <= REG_F31 && ins.rd >= REG_X0 && ins.rd <= REG_X31 {
|
|
ins.as = AFMVXD
|
|
} else {
|
|
ins.as, ins.rs1 = AFSGNJD, uint32(p.From.Reg)
|
|
}
|
|
case AMOVB, AMOVH:
|
|
if buildcfg.GORISCV64 >= 22 {
|
|
// Use SEXTB or SEXTH to extend.
|
|
ins.as, ins.rs1, ins.rs2 = ASEXTB, uint32(p.From.Reg), obj.REG_NONE
|
|
if p.As == AMOVH {
|
|
ins.as = ASEXTH
|
|
}
|
|
} else {
|
|
// Use SLLI/SRAI sequence to extend.
|
|
ins.as, ins.rs1, ins.rs2 = ASLLI, uint32(p.From.Reg), obj.REG_NONE
|
|
if p.As == AMOVB {
|
|
ins.imm = 56
|
|
} else if p.As == AMOVH {
|
|
ins.imm = 48
|
|
}
|
|
ins2 := &instruction{as: ASRAI, rd: ins.rd, rs1: ins.rd, imm: ins.imm}
|
|
inss = append(inss, ins2)
|
|
}
|
|
case AMOVHU, AMOVWU:
|
|
if buildcfg.GORISCV64 >= 22 {
|
|
// Use ZEXTH or ADDUW to extend.
|
|
ins.as, ins.rs1, ins.rs2, ins.imm = AZEXTH, uint32(p.From.Reg), obj.REG_NONE, 0
|
|
if p.As == AMOVWU {
|
|
ins.as, ins.rs2 = AADDUW, REG_ZERO
|
|
}
|
|
} else {
|
|
// Use SLLI/SRLI sequence to extend.
|
|
ins.as, ins.rs1, ins.rs2 = ASLLI, uint32(p.From.Reg), obj.REG_NONE
|
|
if p.As == AMOVHU {
|
|
ins.imm = 48
|
|
} else if p.As == AMOVWU {
|
|
ins.imm = 32
|
|
}
|
|
ins2 := &instruction{as: ASRLI, rd: ins.rd, rs1: ins.rd, imm: ins.imm}
|
|
inss = append(inss, ins2)
|
|
}
|
|
}
|
|
|
|
case p.From.Type == obj.TYPE_MEM && p.To.Type == obj.TYPE_REG:
|
|
// Memory to register loads.
|
|
switch p.From.Name {
|
|
case obj.NAME_AUTO, obj.NAME_PARAM, obj.NAME_NONE:
|
|
// MOV c(Rs), Rd -> L $c, Rs, Rd
|
|
inss = instructionsForLoad(p, movToLoad(p.As), addrToReg(p.From))
|
|
|
|
case obj.NAME_EXTERN, obj.NAME_STATIC, obj.NAME_GOTREF:
|
|
if p.From.Sym.Type == objabi.STLSBSS {
|
|
return instructionsForTLSLoad(p)
|
|
}
|
|
|
|
// Note that the values for $off_hi and $off_lo are currently
|
|
// zero and will be assigned during relocation. If the destination
|
|
// is an integer register then we can use the same register for the
|
|
// address computation, otherwise we need to use the temporary register.
|
|
//
|
|
// AUIPC $off_hi, Rd
|
|
// L $off_lo, Rd, Rd
|
|
//
|
|
addrReg := ins.rd
|
|
if addrReg < REG_X0 || addrReg > REG_X31 {
|
|
addrReg = REG_TMP
|
|
}
|
|
insAUIPC := &instruction{as: AAUIPC, rd: addrReg}
|
|
ins.as, ins.rs1, ins.rs2, ins.imm = movToLoad(p.As), addrReg, obj.REG_NONE, 0
|
|
inss = []*instruction{insAUIPC, ins}
|
|
|
|
default:
|
|
p.Ctxt.Diag("unsupported name %d for %v", p.From.Name, p)
|
|
return nil
|
|
}
|
|
|
|
case p.From.Type == obj.TYPE_REG && p.To.Type == obj.TYPE_MEM:
|
|
// Register to memory stores.
|
|
switch p.As {
|
|
case AMOVBU, AMOVHU, AMOVWU:
|
|
p.Ctxt.Diag("%v: unsupported unsigned store", p)
|
|
return nil
|
|
}
|
|
switch p.To.Name {
|
|
case obj.NAME_AUTO, obj.NAME_PARAM, obj.NAME_NONE:
|
|
// MOV Rs, c(Rd) -> S $c, Rs, Rd
|
|
inss = instructionsForStore(p, movToStore(p.As), addrToReg(p.To))
|
|
|
|
case obj.NAME_EXTERN, obj.NAME_STATIC:
|
|
if p.To.Sym.Type == objabi.STLSBSS {
|
|
return instructionsForTLSStore(p)
|
|
}
|
|
|
|
// Note that the values for $off_hi and $off_lo are currently
|
|
// zero and will be assigned during relocation.
|
|
//
|
|
// AUIPC $off_hi, Rtmp
|
|
// S $off_lo, Rtmp, Rd
|
|
insAUIPC := &instruction{as: AAUIPC, rd: REG_TMP}
|
|
ins.as, ins.rd, ins.rs1, ins.rs2, ins.imm = movToStore(p.As), REG_TMP, uint32(p.From.Reg), obj.REG_NONE, 0
|
|
inss = []*instruction{insAUIPC, ins}
|
|
|
|
default:
|
|
p.Ctxt.Diag("unsupported name %d for %v", p.From.Name, p)
|
|
return nil
|
|
}
|
|
|
|
case p.From.Type == obj.TYPE_ADDR && p.To.Type == obj.TYPE_REG:
|
|
// MOV $sym+off(SP/SB), R
|
|
if p.As != AMOV {
|
|
p.Ctxt.Diag("%v: unsupported address load", p)
|
|
return nil
|
|
}
|
|
switch p.From.Name {
|
|
case obj.NAME_AUTO, obj.NAME_PARAM, obj.NAME_NONE:
|
|
inss = instructionsForOpImmediate(p, AADDI, addrToReg(p.From))
|
|
|
|
case obj.NAME_EXTERN, obj.NAME_STATIC:
|
|
// Note that the values for $off_hi and $off_lo are currently
|
|
// zero and will be assigned during relocation.
|
|
//
|
|
// AUIPC $off_hi, R
|
|
// ADDI $off_lo, R
|
|
insAUIPC := &instruction{as: AAUIPC, rd: ins.rd}
|
|
ins.as, ins.rs1, ins.rs2, ins.imm = AADDI, ins.rd, obj.REG_NONE, 0
|
|
inss = []*instruction{insAUIPC, ins}
|
|
|
|
default:
|
|
p.Ctxt.Diag("unsupported name %d for %v", p.From.Name, p)
|
|
return nil
|
|
}
|
|
|
|
case p.From.Type == obj.TYPE_ADDR && p.To.Type != obj.TYPE_REG:
|
|
p.Ctxt.Diag("%v: address load must target register", p)
|
|
return nil
|
|
|
|
default:
|
|
p.Ctxt.Diag("%v: unsupported MOV", p)
|
|
return nil
|
|
}
|
|
|
|
return inss
|
|
}
|
|
|
|
// instructionsForRotate returns the machine instructions for a bitwise rotation.
|
|
func instructionsForRotate(p *obj.Prog, ins *instruction) []*instruction {
|
|
if buildcfg.GORISCV64 >= 22 {
|
|
// Rotation instructions are supported natively.
|
|
return []*instruction{ins}
|
|
}
|
|
|
|
switch ins.as {
|
|
case AROL, AROLW, AROR, ARORW:
|
|
// ROL -> OR (SLL x y) (SRL x (NEG y))
|
|
// ROR -> OR (SRL x y) (SLL x (NEG y))
|
|
sllOp, srlOp := ASLL, ASRL
|
|
if ins.as == AROLW || ins.as == ARORW {
|
|
sllOp, srlOp = ASLLW, ASRLW
|
|
}
|
|
shift1, shift2 := sllOp, srlOp
|
|
if ins.as == AROR || ins.as == ARORW {
|
|
shift1, shift2 = shift2, shift1
|
|
}
|
|
return []*instruction{
|
|
&instruction{as: ASUB, rs1: REG_ZERO, rs2: ins.rs2, rd: REG_TMP},
|
|
&instruction{as: shift2, rs1: ins.rs1, rs2: REG_TMP, rd: REG_TMP},
|
|
&instruction{as: shift1, rs1: ins.rs1, rs2: ins.rs2, rd: ins.rd},
|
|
&instruction{as: AOR, rs1: REG_TMP, rs2: ins.rd, rd: ins.rd},
|
|
}
|
|
|
|
case ARORI, ARORIW:
|
|
// ROR -> OR (SLLI -x y) (SRLI x y)
|
|
sllOp, srlOp := ASLLI, ASRLI
|
|
sllImm := int64(int8(-ins.imm) & 63)
|
|
if ins.as == ARORIW {
|
|
sllOp, srlOp = ASLLIW, ASRLIW
|
|
sllImm = int64(int8(-ins.imm) & 31)
|
|
}
|
|
return []*instruction{
|
|
&instruction{as: srlOp, rs1: ins.rs1, rd: REG_TMP, imm: ins.imm},
|
|
&instruction{as: sllOp, rs1: ins.rs1, rd: ins.rd, imm: sllImm},
|
|
&instruction{as: AOR, rs1: REG_TMP, rs2: ins.rd, rd: ins.rd},
|
|
}
|
|
|
|
default:
|
|
p.Ctxt.Diag("%v: unknown rotation", p)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
// instructionsForMinMax returns the machine instructions for an integer minimum or maximum.
|
|
func instructionsForMinMax(p *obj.Prog, ins *instruction) []*instruction {
|
|
if buildcfg.GORISCV64 >= 22 {
|
|
// Minimum and maximum instructions are supported natively.
|
|
return []*instruction{ins}
|
|
}
|
|
|
|
// Generate a move for identical inputs.
|
|
if ins.rs1 == ins.rs2 {
|
|
ins.as, ins.rs2, ins.imm = AADDI, obj.REG_NONE, 0
|
|
return []*instruction{ins}
|
|
}
|
|
|
|
// Ensure that if one of the source registers is the same as the destination,
|
|
// it is processed first.
|
|
if ins.rs1 == ins.rd {
|
|
ins.rs1, ins.rs2 = ins.rs2, ins.rs1
|
|
}
|
|
sltReg1, sltReg2 := ins.rs2, ins.rs1
|
|
|
|
// MIN -> SLT/SUB/XOR/AND/XOR
|
|
// MAX -> SLT/SUB/XOR/AND/XOR with swapped inputs to SLT
|
|
switch ins.as {
|
|
case AMIN:
|
|
ins.as = ASLT
|
|
case AMAX:
|
|
ins.as, sltReg1, sltReg2 = ASLT, sltReg2, sltReg1
|
|
case AMINU:
|
|
ins.as = ASLTU
|
|
case AMAXU:
|
|
ins.as, sltReg1, sltReg2 = ASLTU, sltReg2, sltReg1
|
|
}
|
|
return []*instruction{
|
|
&instruction{as: ins.as, rs1: sltReg1, rs2: sltReg2, rd: REG_TMP},
|
|
&instruction{as: ASUB, rs1: REG_ZERO, rs2: REG_TMP, rd: REG_TMP},
|
|
&instruction{as: AXOR, rs1: ins.rs1, rs2: ins.rs2, rd: ins.rd},
|
|
&instruction{as: AAND, rs1: REG_TMP, rs2: ins.rd, rd: ins.rd},
|
|
&instruction{as: AXOR, rs1: ins.rs1, rs2: ins.rd, rd: ins.rd},
|
|
}
|
|
}
|
|
|
|
// instructionsForProg returns the machine instructions for an *obj.Prog.
|
|
func instructionsForProg(p *obj.Prog, compress bool) []*instruction {
|
|
ins := instructionForProg(p)
|
|
inss := []*instruction{ins}
|
|
|
|
if ins.as == AVSETVLI || ins.as == AVSETIVLI {
|
|
if len(p.RestArgs) != 4 {
|
|
p.Ctxt.Diag("incorrect number of arguments for instruction")
|
|
return nil
|
|
}
|
|
} else if len(p.RestArgs) > 1 {
|
|
p.Ctxt.Diag("too many source registers")
|
|
return nil
|
|
}
|
|
|
|
switch ins.as {
|
|
case ACJALR, AJAL, AJALR:
|
|
ins.rd, ins.rs1, ins.rs2 = uint32(p.From.Reg), uint32(p.To.Reg), obj.REG_NONE
|
|
ins.imm = p.To.Offset
|
|
|
|
case ABEQ, ABEQZ, ABGE, ABGEU, ABGEZ, ABGT, ABGTU, ABGTZ, ABLE, ABLEU, ABLEZ, ABLT, ABLTU, ABLTZ, ABNE, ABNEZ:
|
|
switch ins.as {
|
|
case ABEQZ:
|
|
ins.as, ins.rs1, ins.rs2 = ABEQ, REG_ZERO, uint32(p.From.Reg)
|
|
case ABGEZ:
|
|
ins.as, ins.rs1, ins.rs2 = ABGE, REG_ZERO, uint32(p.From.Reg)
|
|
case ABGT:
|
|
ins.as, ins.rs1, ins.rs2 = ABLT, uint32(p.From.Reg), uint32(p.Reg)
|
|
case ABGTU:
|
|
ins.as, ins.rs1, ins.rs2 = ABLTU, uint32(p.From.Reg), uint32(p.Reg)
|
|
case ABGTZ:
|
|
ins.as, ins.rs1, ins.rs2 = ABLT, uint32(p.From.Reg), REG_ZERO
|
|
case ABLE:
|
|
ins.as, ins.rs1, ins.rs2 = ABGE, uint32(p.From.Reg), uint32(p.Reg)
|
|
case ABLEU:
|
|
ins.as, ins.rs1, ins.rs2 = ABGEU, uint32(p.From.Reg), uint32(p.Reg)
|
|
case ABLEZ:
|
|
ins.as, ins.rs1, ins.rs2 = ABGE, uint32(p.From.Reg), REG_ZERO
|
|
case ABLTZ:
|
|
ins.as, ins.rs1, ins.rs2 = ABLT, REG_ZERO, uint32(p.From.Reg)
|
|
case ABNEZ:
|
|
ins.as, ins.rs1, ins.rs2 = ABNE, REG_ZERO, uint32(p.From.Reg)
|
|
}
|
|
ins.imm = p.To.Offset
|
|
|
|
case AMOV, AMOVB, AMOVH, AMOVW, AMOVBU, AMOVHU, AMOVWU, AMOVF, AMOVD:
|
|
inss = instructionsForMOV(p)
|
|
|
|
case ALW, ALWU, ALH, ALHU, ALB, ALBU, ALD, AFLW, AFLD:
|
|
inss = instructionsForLoad(p, ins.as, p.From.Reg)
|
|
|
|
case ASW, ASH, ASB, ASD, AFSW, AFSD:
|
|
inss = instructionsForStore(p, ins.as, p.To.Reg)
|
|
|
|
case ALRW, ALRD:
|
|
// Set aq to use acquire access ordering
|
|
ins.funct7 = 2
|
|
ins.rs1, ins.rs2 = uint32(p.From.Reg), REG_ZERO
|
|
|
|
case AADDI, AANDI, AORI, AXORI:
|
|
inss = instructionsForOpImmediate(p, ins.as, p.Reg)
|
|
|
|
case ASCW, ASCD:
|
|
// Set release access ordering
|
|
ins.funct7 = 1
|
|
ins.rd, ins.rs1, ins.rs2 = uint32(p.RegTo2), uint32(p.To.Reg), uint32(p.From.Reg)
|
|
|
|
case AAMOSWAPW, AAMOSWAPD, AAMOADDW, AAMOADDD, AAMOANDW, AAMOANDD, AAMOORW, AAMOORD,
|
|
AAMOXORW, AAMOXORD, AAMOMINW, AAMOMIND, AAMOMINUW, AAMOMINUD, AAMOMAXW, AAMOMAXD, AAMOMAXUW, AAMOMAXUD:
|
|
// Set aqrl to use acquire & release access ordering
|
|
ins.funct7 = 3
|
|
ins.rd, ins.rs1, ins.rs2 = uint32(p.RegTo2), uint32(p.To.Reg), uint32(p.From.Reg)
|
|
|
|
case AECALL, AEBREAK:
|
|
insEnc := encode(p.As)
|
|
if p.To.Type == obj.TYPE_NONE {
|
|
ins.rd = REG_ZERO
|
|
}
|
|
ins.rs1 = REG_ZERO
|
|
ins.imm = insEnc.csr
|
|
|
|
case ARDCYCLE, ARDTIME, ARDINSTRET:
|
|
ins.as = ACSRRS
|
|
if p.To.Type == obj.TYPE_NONE {
|
|
ins.rd = REG_ZERO
|
|
}
|
|
ins.rs1 = REG_ZERO
|
|
switch p.As {
|
|
case ARDCYCLE:
|
|
ins.imm = -1024
|
|
case ARDTIME:
|
|
ins.imm = -1023
|
|
case ARDINSTRET:
|
|
ins.imm = -1022
|
|
}
|
|
|
|
case ACSRRC, ACSRRCI, ACSRRS, ACSRRSI, ACSRRW, ACSRRWI:
|
|
if len(p.RestArgs) == 0 || p.RestArgs[0].Type != obj.TYPE_SPECIAL {
|
|
p.Ctxt.Diag("%v: missing CSR name", p)
|
|
return nil
|
|
}
|
|
if p.From.Type == obj.TYPE_CONST {
|
|
imm := p.From.Offset
|
|
if imm < 0 || imm >= 32 {
|
|
p.Ctxt.Diag("%v: immediate out of range 0 to 31", p)
|
|
return nil
|
|
}
|
|
ins.rs1 = uint32(imm) + REG_ZERO
|
|
} else if p.From.Type == obj.TYPE_REG {
|
|
ins.rs1 = uint32(p.From.Reg)
|
|
} else {
|
|
p.Ctxt.Diag("%v: integer register or immediate expected for 1st operand", p)
|
|
return nil
|
|
}
|
|
if p.To.Type != obj.TYPE_REG {
|
|
p.Ctxt.Diag("%v: needs an integer register output", p)
|
|
return nil
|
|
}
|
|
csrNum := SpecialOperand(p.RestArgs[0].Offset).encode()
|
|
if csrNum >= 1<<12 {
|
|
p.Ctxt.Diag("%v: unknown CSR", p)
|
|
return nil
|
|
}
|
|
if _, ok := CSRs[uint16(csrNum)]; !ok {
|
|
p.Ctxt.Diag("%v: unknown CSR", p)
|
|
return nil
|
|
}
|
|
ins.imm = int64(csrNum)
|
|
if ins.imm > 2047 {
|
|
ins.imm -= 4096
|
|
}
|
|
ins.rs2 = obj.REG_NONE
|
|
|
|
case AFENCE:
|
|
ins.rd, ins.rs1, ins.rs2 = REG_ZERO, REG_ZERO, obj.REG_NONE
|
|
ins.imm = 0x0ff
|
|
|
|
case AFCVTWS, AFCVTLS, AFCVTWUS, AFCVTLUS, AFCVTWD, AFCVTLD, AFCVTWUD, AFCVTLUD:
|
|
// Set the default rounding mode in funct3 to round to zero.
|
|
if p.Scond&rmSuffixBit == 0 {
|
|
ins.funct3 = uint32(RM_RTZ)
|
|
} else {
|
|
ins.funct3 = uint32(p.Scond &^ rmSuffixBit)
|
|
}
|
|
|
|
case AFNES, AFNED:
|
|
// Replace FNE[SD] with FEQ[SD] and NOT.
|
|
if p.To.Type != obj.TYPE_REG {
|
|
p.Ctxt.Diag("%v needs an integer register output", p)
|
|
return nil
|
|
}
|
|
if ins.as == AFNES {
|
|
ins.as = AFEQS
|
|
} else {
|
|
ins.as = AFEQD
|
|
}
|
|
ins2 := &instruction{
|
|
as: AXORI, // [bit] xor 1 = not [bit]
|
|
rd: ins.rd,
|
|
rs1: ins.rd,
|
|
imm: 1,
|
|
}
|
|
inss = append(inss, ins2)
|
|
|
|
case AFSQRTS, AFSQRTD:
|
|
// These instructions expect a zero (i.e. float register 0)
|
|
// to be the second input operand.
|
|
ins.rs1 = uint32(p.From.Reg)
|
|
ins.rs2 = REG_F0
|
|
|
|
case AFMADDS, AFMSUBS, AFNMADDS, AFNMSUBS,
|
|
AFMADDD, AFMSUBD, AFNMADDD, AFNMSUBD:
|
|
// Swap the first two operands so that the operands are in the same
|
|
// order as they are in the specification: RS1, RS2, RS3, RD.
|
|
ins.rs1, ins.rs2 = ins.rs2, ins.rs1
|
|
|
|
case ANEG, ANEGW:
|
|
// NEG rs, rd -> SUB rs, X0, rd
|
|
ins.as = ASUB
|
|
if p.As == ANEGW {
|
|
ins.as = ASUBW
|
|
}
|
|
ins.rs1 = REG_ZERO
|
|
if ins.rd == obj.REG_NONE {
|
|
ins.rd = ins.rs2
|
|
}
|
|
|
|
case ANOT:
|
|
// NOT rs, rd -> XORI $-1, rs, rd
|
|
ins.as = AXORI
|
|
ins.rs1, ins.rs2 = uint32(p.From.Reg), obj.REG_NONE
|
|
if ins.rd == obj.REG_NONE {
|
|
ins.rd = ins.rs1
|
|
}
|
|
ins.imm = -1
|
|
|
|
case ASEQZ:
|
|
// SEQZ rs, rd -> SLTIU $1, rs, rd
|
|
ins.as = ASLTIU
|
|
ins.rs1, ins.rs2 = uint32(p.From.Reg), obj.REG_NONE
|
|
ins.imm = 1
|
|
|
|
case ASNEZ:
|
|
// SNEZ rs, rd -> SLTU rs, x0, rd
|
|
ins.as = ASLTU
|
|
ins.rs1 = REG_ZERO
|
|
|
|
case AFABSS:
|
|
// FABSS rs, rd -> FSGNJXS rs, rs, rd
|
|
ins.as = AFSGNJXS
|
|
ins.rs1 = uint32(p.From.Reg)
|
|
|
|
case AFABSD:
|
|
// FABSD rs, rd -> FSGNJXD rs, rs, rd
|
|
ins.as = AFSGNJXD
|
|
ins.rs1 = uint32(p.From.Reg)
|
|
|
|
case AFNEGS:
|
|
// FNEGS rs, rd -> FSGNJNS rs, rs, rd
|
|
ins.as = AFSGNJNS
|
|
ins.rs1 = uint32(p.From.Reg)
|
|
|
|
case AFNEGD:
|
|
// FNEGD rs, rd -> FSGNJND rs, rs, rd
|
|
ins.as = AFSGNJND
|
|
ins.rs1 = uint32(p.From.Reg)
|
|
|
|
case ACLW, ACLD, ACFLD:
|
|
ins.rs1, ins.rs2 = ins.rs2, obj.REG_NONE
|
|
|
|
case ACSW, ACSD, ACFSD:
|
|
ins.rs1, ins.rd = ins.rd, obj.REG_NONE
|
|
ins.imm = p.To.Offset
|
|
|
|
case ACSWSP, ACSDSP, ACFSDSP:
|
|
ins.imm = p.To.Offset
|
|
|
|
case ACANDI, ACSRLI, ACSRAI:
|
|
ins.rs1, ins.rd = ins.rd, ins.rs1
|
|
|
|
case ACBEQZ, ACBNEZ:
|
|
ins.rd, ins.rs1, ins.rs2 = obj.REG_NONE, uint32(p.From.Reg), obj.REG_NONE
|
|
ins.imm = p.To.Offset
|
|
|
|
case ACJR:
|
|
ins.rd, ins.rs1 = obj.REG_NONE, uint32(p.To.Reg)
|
|
|
|
case ACJ:
|
|
ins.imm = p.To.Offset
|
|
|
|
case ACNOP:
|
|
ins.rd, ins.rs1 = REG_ZERO, REG_ZERO
|
|
|
|
case AROL, AROLW, AROR, ARORW:
|
|
inss = instructionsForRotate(p, ins)
|
|
|
|
case ARORI:
|
|
if ins.imm < 0 || ins.imm > 63 {
|
|
p.Ctxt.Diag("%v: immediate out of range 0 to 63", p)
|
|
}
|
|
inss = instructionsForRotate(p, ins)
|
|
|
|
case ARORIW:
|
|
if ins.imm < 0 || ins.imm > 31 {
|
|
p.Ctxt.Diag("%v: immediate out of range 0 to 31", p)
|
|
}
|
|
inss = instructionsForRotate(p, ins)
|
|
|
|
case ASLLI, ASRLI, ASRAI:
|
|
if ins.imm < 0 || ins.imm > 63 {
|
|
p.Ctxt.Diag("%v: immediate out of range 0 to 63", p)
|
|
}
|
|
|
|
case ASLLIW, ASRLIW, ASRAIW:
|
|
if ins.imm < 0 || ins.imm > 31 {
|
|
p.Ctxt.Diag("%v: immediate out of range 0 to 31", p)
|
|
}
|
|
|
|
case ACLZ, ACLZW, ACTZ, ACTZW, ACPOP, ACPOPW, ASEXTB, ASEXTH, AZEXTH:
|
|
ins.rs1, ins.rs2 = uint32(p.From.Reg), obj.REG_NONE
|
|
|
|
case AORCB, AREV8:
|
|
ins.rd, ins.rs1, ins.rs2 = uint32(p.To.Reg), uint32(p.From.Reg), obj.REG_NONE
|
|
|
|
case AANDN, AORN:
|
|
if buildcfg.GORISCV64 >= 22 {
|
|
// ANDN and ORN instructions are supported natively.
|
|
break
|
|
}
|
|
// ANDN -> (AND (NOT x) y)
|
|
// ORN -> (OR (NOT x) y)
|
|
bitwiseOp, notReg := AAND, ins.rd
|
|
if ins.as == AORN {
|
|
bitwiseOp = AOR
|
|
}
|
|
if ins.rs1 == notReg {
|
|
notReg = REG_TMP
|
|
}
|
|
inss = []*instruction{
|
|
&instruction{as: AXORI, rs1: ins.rs2, rs2: obj.REG_NONE, rd: notReg, imm: -1},
|
|
&instruction{as: bitwiseOp, rs1: ins.rs1, rs2: notReg, rd: ins.rd},
|
|
}
|
|
|
|
case AXNOR:
|
|
if buildcfg.GORISCV64 >= 22 {
|
|
// XNOR instruction is supported natively.
|
|
break
|
|
}
|
|
// XNOR -> (NOT (XOR x y))
|
|
ins.as = AXOR
|
|
inss = append(inss, &instruction{as: AXORI, rs1: ins.rd, rs2: obj.REG_NONE, rd: ins.rd, imm: -1})
|
|
|
|
case AMIN, AMAX, AMINU, AMAXU:
|
|
inss = instructionsForMinMax(p, ins)
|
|
|
|
case AVSETVLI, AVSETIVLI:
|
|
ins.rs1, ins.rs2 = ins.rs2, obj.REG_NONE
|
|
vtype, err := EncodeVectorType(p.RestArgs[0].Offset, p.RestArgs[1].Offset, p.RestArgs[2].Offset, p.RestArgs[3].Offset)
|
|
if err != nil {
|
|
p.Ctxt.Diag("%v: %v", p, err)
|
|
}
|
|
ins.imm = vtype
|
|
if ins.as == AVSETIVLI {
|
|
if p.From.Type != obj.TYPE_CONST {
|
|
p.Ctxt.Diag("%v: expected immediate value", p)
|
|
}
|
|
ins.rs1 = uint32(p.From.Offset)
|
|
}
|
|
|
|
case AVLE8V, AVLE16V, AVLE32V, AVLE64V, AVSE8V, AVSE16V, AVSE32V, AVSE64V, AVLE8FFV, AVLE16FFV, AVLE32FFV, AVLE64FFV, AVLMV, AVSMV,
|
|
AVLSEG2E8V, AVLSEG3E8V, AVLSEG4E8V, AVLSEG5E8V, AVLSEG6E8V, AVLSEG7E8V, AVLSEG8E8V,
|
|
AVLSEG2E16V, AVLSEG3E16V, AVLSEG4E16V, AVLSEG5E16V, AVLSEG6E16V, AVLSEG7E16V, AVLSEG8E16V,
|
|
AVLSEG2E32V, AVLSEG3E32V, AVLSEG4E32V, AVLSEG5E32V, AVLSEG6E32V, AVLSEG7E32V, AVLSEG8E32V,
|
|
AVLSEG2E64V, AVLSEG3E64V, AVLSEG4E64V, AVLSEG5E64V, AVLSEG6E64V, AVLSEG7E64V, AVLSEG8E64V,
|
|
AVSSEG2E8V, AVSSEG3E8V, AVSSEG4E8V, AVSSEG5E8V, AVSSEG6E8V, AVSSEG7E8V, AVSSEG8E8V,
|
|
AVSSEG2E16V, AVSSEG3E16V, AVSSEG4E16V, AVSSEG5E16V, AVSSEG6E16V, AVSSEG7E16V, AVSSEG8E16V,
|
|
AVSSEG2E32V, AVSSEG3E32V, AVSSEG4E32V, AVSSEG5E32V, AVSSEG6E32V, AVSSEG7E32V, AVSSEG8E32V,
|
|
AVSSEG2E64V, AVSSEG3E64V, AVSSEG4E64V, AVSSEG5E64V, AVSSEG6E64V, AVSSEG7E64V, AVSSEG8E64V,
|
|
AVLSEG2E8FFV, AVLSEG3E8FFV, AVLSEG4E8FFV, AVLSEG5E8FFV, AVLSEG6E8FFV, AVLSEG7E8FFV, AVLSEG8E8FFV,
|
|
AVLSEG2E16FFV, AVLSEG3E16FFV, AVLSEG4E16FFV, AVLSEG5E16FFV, AVLSEG6E16FFV, AVLSEG7E16FFV, AVLSEG8E16FFV,
|
|
AVLSEG2E32FFV, AVLSEG3E32FFV, AVLSEG4E32FFV, AVLSEG5E32FFV, AVLSEG6E32FFV, AVLSEG7E32FFV, AVLSEG8E32FFV,
|
|
AVLSEG2E64FFV, AVLSEG3E64FFV, AVLSEG4E64FFV, AVLSEG5E64FFV, AVLSEG6E64FFV, AVLSEG7E64FFV, AVLSEG8E64FFV:
|
|
// Set mask bit
|
|
switch {
|
|
case ins.rs1 == obj.REG_NONE:
|
|
ins.funct7 |= 1 // unmasked
|
|
case ins.rs1 != REG_V0:
|
|
p.Ctxt.Diag("%v: invalid vector mask register", p)
|
|
}
|
|
ins.rd, ins.rs1, ins.rs2 = uint32(p.To.Reg), uint32(p.From.Reg), obj.REG_NONE
|
|
|
|
case AVLSE8V, AVLSE16V, AVLSE32V, AVLSE64V,
|
|
AVLUXEI8V, AVLUXEI16V, AVLUXEI32V, AVLUXEI64V, AVLOXEI8V, AVLOXEI16V, AVLOXEI32V, AVLOXEI64V,
|
|
AVLSSEG2E8V, AVLSSEG3E8V, AVLSSEG4E8V, AVLSSEG5E8V, AVLSSEG6E8V, AVLSSEG7E8V, AVLSSEG8E8V,
|
|
AVLSSEG2E16V, AVLSSEG3E16V, AVLSSEG4E16V, AVLSSEG5E16V, AVLSSEG6E16V, AVLSSEG7E16V, AVLSSEG8E16V,
|
|
AVLSSEG2E32V, AVLSSEG3E32V, AVLSSEG4E32V, AVLSSEG5E32V, AVLSSEG6E32V, AVLSSEG7E32V, AVLSSEG8E32V,
|
|
AVLSSEG2E64V, AVLSSEG3E64V, AVLSSEG4E64V, AVLSSEG5E64V, AVLSSEG6E64V, AVLSSEG7E64V, AVLSSEG8E64V,
|
|
AVLOXSEG2EI8V, AVLOXSEG3EI8V, AVLOXSEG4EI8V, AVLOXSEG5EI8V, AVLOXSEG6EI8V, AVLOXSEG7EI8V, AVLOXSEG8EI8V,
|
|
AVLOXSEG2EI16V, AVLOXSEG3EI16V, AVLOXSEG4EI16V, AVLOXSEG5EI16V, AVLOXSEG6EI16V, AVLOXSEG7EI16V, AVLOXSEG8EI16V,
|
|
AVLOXSEG2EI32V, AVLOXSEG3EI32V, AVLOXSEG4EI32V, AVLOXSEG5EI32V, AVLOXSEG6EI32V, AVLOXSEG7EI32V, AVLOXSEG8EI32V,
|
|
AVLOXSEG2EI64V, AVLOXSEG3EI64V, AVLOXSEG4EI64V, AVLOXSEG5EI64V, AVLOXSEG6EI64V, AVLOXSEG7EI64V, AVLOXSEG8EI64V,
|
|
AVLUXSEG2EI8V, AVLUXSEG3EI8V, AVLUXSEG4EI8V, AVLUXSEG5EI8V, AVLUXSEG6EI8V, AVLUXSEG7EI8V, AVLUXSEG8EI8V,
|
|
AVLUXSEG2EI16V, AVLUXSEG3EI16V, AVLUXSEG4EI16V, AVLUXSEG5EI16V, AVLUXSEG6EI16V, AVLUXSEG7EI16V, AVLUXSEG8EI16V,
|
|
AVLUXSEG2EI32V, AVLUXSEG3EI32V, AVLUXSEG4EI32V, AVLUXSEG5EI32V, AVLUXSEG6EI32V, AVLUXSEG7EI32V, AVLUXSEG8EI32V,
|
|
AVLUXSEG2EI64V, AVLUXSEG3EI64V, AVLUXSEG4EI64V, AVLUXSEG5EI64V, AVLUXSEG6EI64V, AVLUXSEG7EI64V, AVLUXSEG8EI64V:
|
|
// Set mask bit
|
|
switch {
|
|
case ins.rs3 == obj.REG_NONE:
|
|
ins.funct7 |= 1 // unmasked
|
|
case ins.rs3 != REG_V0:
|
|
p.Ctxt.Diag("%v: invalid vector mask register", p)
|
|
}
|
|
ins.rs1, ins.rs2, ins.rs3 = ins.rs2, ins.rs1, obj.REG_NONE
|
|
|
|
case AVSSE8V, AVSSE16V, AVSSE32V, AVSSE64V,
|
|
AVSUXEI8V, AVSUXEI16V, AVSUXEI32V, AVSUXEI64V, AVSOXEI8V, AVSOXEI16V, AVSOXEI32V, AVSOXEI64V,
|
|
AVSSSEG2E8V, AVSSSEG3E8V, AVSSSEG4E8V, AVSSSEG5E8V, AVSSSEG6E8V, AVSSSEG7E8V, AVSSSEG8E8V,
|
|
AVSSSEG2E16V, AVSSSEG3E16V, AVSSSEG4E16V, AVSSSEG5E16V, AVSSSEG6E16V, AVSSSEG7E16V, AVSSSEG8E16V,
|
|
AVSSSEG2E32V, AVSSSEG3E32V, AVSSSEG4E32V, AVSSSEG5E32V, AVSSSEG6E32V, AVSSSEG7E32V, AVSSSEG8E32V,
|
|
AVSSSEG2E64V, AVSSSEG3E64V, AVSSSEG4E64V, AVSSSEG5E64V, AVSSSEG6E64V, AVSSSEG7E64V, AVSSSEG8E64V,
|
|
AVSOXSEG2EI8V, AVSOXSEG3EI8V, AVSOXSEG4EI8V, AVSOXSEG5EI8V, AVSOXSEG6EI8V, AVSOXSEG7EI8V, AVSOXSEG8EI8V,
|
|
AVSOXSEG2EI16V, AVSOXSEG3EI16V, AVSOXSEG4EI16V, AVSOXSEG5EI16V, AVSOXSEG6EI16V, AVSOXSEG7EI16V, AVSOXSEG8EI16V,
|
|
AVSOXSEG2EI32V, AVSOXSEG3EI32V, AVSOXSEG4EI32V, AVSOXSEG5EI32V, AVSOXSEG6EI32V, AVSOXSEG7EI32V, AVSOXSEG8EI32V,
|
|
AVSOXSEG2EI64V, AVSOXSEG3EI64V, AVSOXSEG4EI64V, AVSOXSEG5EI64V, AVSOXSEG6EI64V, AVSOXSEG7EI64V, AVSOXSEG8EI64V,
|
|
AVSUXSEG2EI8V, AVSUXSEG3EI8V, AVSUXSEG4EI8V, AVSUXSEG5EI8V, AVSUXSEG6EI8V, AVSUXSEG7EI8V, AVSUXSEG8EI8V,
|
|
AVSUXSEG2EI16V, AVSUXSEG3EI16V, AVSUXSEG4EI16V, AVSUXSEG5EI16V, AVSUXSEG6EI16V, AVSUXSEG7EI16V, AVSUXSEG8EI16V,
|
|
AVSUXSEG2EI32V, AVSUXSEG3EI32V, AVSUXSEG4EI32V, AVSUXSEG5EI32V, AVSUXSEG6EI32V, AVSUXSEG7EI32V, AVSUXSEG8EI32V,
|
|
AVSUXSEG2EI64V, AVSUXSEG3EI64V, AVSUXSEG4EI64V, AVSUXSEG5EI64V, AVSUXSEG6EI64V, AVSUXSEG7EI64V, AVSUXSEG8EI64V:
|
|
// Set mask bit
|
|
switch {
|
|
case ins.rs3 == obj.REG_NONE:
|
|
ins.funct7 |= 1 // unmasked
|
|
case ins.rs3 != REG_V0:
|
|
p.Ctxt.Diag("%v: invalid vector mask register", p)
|
|
}
|
|
ins.rd, ins.rs1, ins.rs2, ins.rs3 = ins.rs2, ins.rd, ins.rs1, obj.REG_NONE
|
|
|
|
case AVL1RV, AVL1RE8V, AVL1RE16V, AVL1RE32V, AVL1RE64V, AVL2RV, AVL2RE8V, AVL2RE16V, AVL2RE32V, AVL2RE64V,
|
|
AVL4RV, AVL4RE8V, AVL4RE16V, AVL4RE32V, AVL4RE64V, AVL8RV, AVL8RE8V, AVL8RE16V, AVL8RE32V, AVL8RE64V:
|
|
switch ins.as {
|
|
case AVL1RV:
|
|
ins.as = AVL1RE8V
|
|
case AVL2RV:
|
|
ins.as = AVL2RE8V
|
|
case AVL4RV:
|
|
ins.as = AVL4RE8V
|
|
case AVL8RV:
|
|
ins.as = AVL8RE8V
|
|
}
|
|
if ins.rs1 != obj.REG_NONE {
|
|
p.Ctxt.Diag("%v: too many operands for instruction", p)
|
|
}
|
|
ins.rd, ins.rs1, ins.rs2 = uint32(p.To.Reg), uint32(p.From.Reg), obj.REG_NONE
|
|
|
|
case AVS1RV, AVS2RV, AVS4RV, AVS8RV:
|
|
if ins.rs1 != obj.REG_NONE {
|
|
p.Ctxt.Diag("%v: too many operands for instruction", p)
|
|
}
|
|
ins.rd, ins.rs1, ins.rs2 = uint32(p.To.Reg), uint32(p.From.Reg), obj.REG_NONE
|
|
|
|
case AVADDVV, AVADDVX, AVSUBVV, AVSUBVX, AVRSUBVX, AVWADDUVV, AVWADDUVX, AVWSUBUVV, AVWSUBUVX,
|
|
AVWADDVV, AVWADDVX, AVWSUBVV, AVWSUBVX, AVWADDUWV, AVWADDUWX, AVWSUBUWV, AVWSUBUWX,
|
|
AVWADDWV, AVWADDWX, AVWSUBWV, AVWSUBWX, AVANDVV, AVANDVX, AVORVV, AVORVX, AVXORVV, AVXORVX,
|
|
AVSLLVV, AVSLLVX, AVSRLVV, AVSRLVX, AVSRAVV, AVSRAVX,
|
|
AVMSEQVV, AVMSEQVX, AVMSNEVV, AVMSNEVX, AVMSLTUVV, AVMSLTUVX, AVMSLTVV, AVMSLTVX,
|
|
AVMSLEUVV, AVMSLEUVX, AVMSLEVV, AVMSLEVX, AVMSGTUVX, AVMSGTVX,
|
|
AVMINUVV, AVMINUVX, AVMINVV, AVMINVX, AVMAXUVV, AVMAXUVX, AVMAXVV, AVMAXVX,
|
|
AVMULVV, AVMULVX, AVMULHVV, AVMULHVX, AVMULHUVV, AVMULHUVX, AVMULHSUVV, AVMULHSUVX,
|
|
AVDIVUVV, AVDIVUVX, AVDIVVV, AVDIVVX, AVREMUVV, AVREMUVX, AVREMVV, AVREMVX,
|
|
AVWMULVV, AVWMULVX, AVWMULUVV, AVWMULUVX, AVWMULSUVV, AVWMULSUVX, AVNSRLWV, AVNSRLWX, AVNSRAWV, AVNSRAWX,
|
|
AVSADDUVV, AVSADDUVX, AVSADDUVI, AVSADDVV, AVSADDVX, AVSADDVI, AVSSUBUVV, AVSSUBUVX, AVSSUBVV, AVSSUBVX,
|
|
AVAADDUVV, AVAADDUVX, AVAADDVV, AVAADDVX, AVASUBUVV, AVASUBUVX, AVASUBVV, AVASUBVX,
|
|
AVSMULVV, AVSMULVX, AVSSRLVV, AVSSRLVX, AVSSRLVI, AVSSRAVV, AVSSRAVX, AVSSRAVI,
|
|
AVNCLIPUWV, AVNCLIPUWX, AVNCLIPUWI, AVNCLIPWV, AVNCLIPWX, AVNCLIPWI,
|
|
AVFADDVV, AVFADDVF, AVFSUBVV, AVFSUBVF, AVFRSUBVF,
|
|
AVFWADDVV, AVFWADDVF, AVFWSUBVV, AVFWSUBVF, AVFWADDWV, AVFWADDWF, AVFWSUBWV, AVFWSUBWF,
|
|
AVFMULVV, AVFMULVF, AVFDIVVV, AVFDIVVF, AVFRDIVVF, AVFWMULVV, AVFWMULVF,
|
|
AVFMINVV, AVFMINVF, AVFMAXVV, AVFMAXVF,
|
|
AVFSGNJVV, AVFSGNJVF, AVFSGNJNVV, AVFSGNJNVF, AVFSGNJXVV, AVFSGNJXVF,
|
|
AVMFEQVV, AVMFEQVF, AVMFNEVV, AVMFNEVF, AVMFLTVV, AVMFLTVF, AVMFLEVV, AVMFLEVF, AVMFGTVF, AVMFGEVF,
|
|
AVREDSUMVS, AVREDMAXUVS, AVREDMAXVS, AVREDMINUVS, AVREDMINVS, AVREDANDVS, AVREDORVS, AVREDXORVS,
|
|
AVWREDSUMUVS, AVWREDSUMVS, AVFREDOSUMVS, AVFREDUSUMVS, AVFREDMAXVS, AVFREDMINVS, AVFWREDOSUMVS, AVFWREDUSUMVS,
|
|
AVSLIDEUPVX, AVSLIDEDOWNVX, AVSLIDE1UPVX, AVFSLIDE1UPVF, AVSLIDE1DOWNVX, AVFSLIDE1DOWNVF,
|
|
AVRGATHERVV, AVRGATHEREI16VV, AVRGATHERVX:
|
|
// Set mask bit
|
|
switch {
|
|
case ins.rs3 == obj.REG_NONE:
|
|
ins.funct7 |= 1 // unmasked
|
|
case ins.rs3 != REG_V0:
|
|
p.Ctxt.Diag("%v: invalid vector mask register", p)
|
|
}
|
|
ins.rd, ins.rs1, ins.rs2, ins.rs3 = uint32(p.To.Reg), uint32(p.From.Reg), uint32(p.Reg), obj.REG_NONE
|
|
|
|
case AVFMACCVV, AVFMACCVF, AVFNMACCVV, AVFNMACCVF, AVFMSACVV, AVFMSACVF, AVFNMSACVV, AVFNMSACVF,
|
|
AVFMADDVV, AVFMADDVF, AVFNMADDVV, AVFNMADDVF, AVFMSUBVV, AVFMSUBVF, AVFNMSUBVV, AVFNMSUBVF,
|
|
AVFWMACCVV, AVFWMACCVF, AVFWNMACCVV, AVFWNMACCVF, AVFWMSACVV, AVFWMSACVF, AVFWNMSACVV, AVFWNMSACVF,
|
|
AVMACCVV, AVMACCVX, AVNMSACVV, AVNMSACVX, AVMADDVV, AVMADDVX, AVNMSUBVV, AVNMSUBVX,
|
|
AVWMACCUVV, AVWMACCUVX, AVWMACCVV, AVWMACCVX, AVWMACCSUVV, AVWMACCSUVX, AVWMACCUSVX:
|
|
switch {
|
|
case ins.rs3 == obj.REG_NONE:
|
|
ins.funct7 |= 1 // unmasked
|
|
case ins.rs3 != REG_V0:
|
|
p.Ctxt.Diag("%v: invalid vector mask register", p)
|
|
}
|
|
ins.rd, ins.rs1, ins.rs2, ins.rs3 = uint32(p.To.Reg), uint32(p.Reg), uint32(p.From.Reg), obj.REG_NONE
|
|
|
|
case AVADDVI, AVRSUBVI, AVANDVI, AVORVI, AVXORVI, AVMSEQVI, AVMSNEVI, AVMSLEUVI, AVMSLEVI, AVMSGTUVI, AVMSGTVI,
|
|
AVSLLVI, AVSRLVI, AVSRAVI, AVNSRLWI, AVNSRAWI, AVRGATHERVI, AVSLIDEUPVI, AVSLIDEDOWNVI:
|
|
// Set mask bit
|
|
switch {
|
|
case ins.rs3 == obj.REG_NONE:
|
|
ins.funct7 |= 1 // unmasked
|
|
case ins.rs3 != REG_V0:
|
|
p.Ctxt.Diag("%v: invalid vector mask register", p)
|
|
}
|
|
ins.rd, ins.rs1, ins.rs2, ins.rs3 = uint32(p.To.Reg), obj.REG_NONE, uint32(p.Reg), obj.REG_NONE
|
|
|
|
case AVZEXTVF2, AVSEXTVF2, AVZEXTVF4, AVSEXTVF4, AVZEXTVF8, AVSEXTVF8, AVFSQRTV, AVFRSQRT7V, AVFREC7V, AVFCLASSV,
|
|
AVFCVTXUFV, AVFCVTXFV, AVFCVTRTZXUFV, AVFCVTRTZXFV, AVFCVTFXUV, AVFCVTFXV,
|
|
AVFWCVTXUFV, AVFWCVTXFV, AVFWCVTRTZXUFV, AVFWCVTRTZXFV, AVFWCVTFXUV, AVFWCVTFXV, AVFWCVTFFV,
|
|
AVFNCVTXUFW, AVFNCVTXFW, AVFNCVTRTZXUFW, AVFNCVTRTZXFW, AVFNCVTFXUW, AVFNCVTFXW, AVFNCVTFFW, AVFNCVTRODFFW:
|
|
// Set mask bit
|
|
switch {
|
|
case ins.rs1 == obj.REG_NONE:
|
|
ins.funct7 |= 1 // unmasked
|
|
case ins.rs1 != REG_V0:
|
|
p.Ctxt.Diag("%v: invalid vector mask register", p)
|
|
}
|
|
ins.rs1 = obj.REG_NONE
|
|
|
|
case AVMVVV, AVMVVX:
|
|
if ins.rs1 != obj.REG_NONE {
|
|
p.Ctxt.Diag("%v: too many operands for instruction", p)
|
|
}
|
|
ins.rd, ins.rs1, ins.rs2 = uint32(p.To.Reg), uint32(p.From.Reg), REG_V0
|
|
|
|
case AVMVVI:
|
|
if ins.rs1 != obj.REG_NONE {
|
|
p.Ctxt.Diag("%v: too many operands for instruction", p)
|
|
}
|
|
ins.rd, ins.rs1, ins.rs2 = uint32(p.To.Reg), obj.REG_NONE, REG_V0
|
|
|
|
case AVFMVVF:
|
|
ins.funct7 |= 1 // unmasked
|
|
ins.rd, ins.rs1, ins.rs2 = uint32(p.To.Reg), uint32(p.From.Reg), REG_V0
|
|
|
|
case AVADCVIM, AVADCVVM, AVADCVXM, AVSBCVVM, AVSBCVXM:
|
|
if ins.rd == REG_V0 {
|
|
p.Ctxt.Diag("%v: invalid destination register V0", p)
|
|
}
|
|
fallthrough
|
|
|
|
case AVMADCVVM, AVMADCVXM, AVMSBCVVM, AVMSBCVXM, AVMADCVIM, AVMERGEVVM, AVMERGEVXM, AVMERGEVIM, AVFMERGEVFM:
|
|
if ins.rs3 != REG_V0 {
|
|
p.Ctxt.Diag("%v: invalid vector mask register", p)
|
|
}
|
|
ins.rd, ins.rs1, ins.rs2, ins.rs3 = uint32(p.To.Reg), uint32(p.From.Reg), uint32(p.Reg), obj.REG_NONE
|
|
|
|
case AVMADCVV, AVMADCVX, AVMSBCVV, AVMSBCVX, AVMADCVI:
|
|
ins.rd, ins.rs1, ins.rs2 = uint32(p.To.Reg), uint32(p.From.Reg), uint32(p.Reg)
|
|
|
|
case AVNEGV, AVWCVTXXV, AVWCVTUXXV, AVNCVTXXW:
|
|
// Set mask bit
|
|
switch {
|
|
case ins.rs1 == obj.REG_NONE:
|
|
ins.funct7 |= 1 // unmasked
|
|
case ins.rs1 != REG_V0:
|
|
p.Ctxt.Diag("%v: invalid vector mask register", p)
|
|
}
|
|
switch ins.as {
|
|
case AVNEGV:
|
|
ins.as = AVRSUBVX
|
|
case AVWCVTXXV:
|
|
ins.as = AVWADDVX
|
|
case AVWCVTUXXV:
|
|
ins.as = AVWADDUVX
|
|
case AVNCVTXXW:
|
|
ins.as = AVNSRLWX
|
|
}
|
|
ins.rd, ins.rs1, ins.rs2 = uint32(p.To.Reg), REG_X0, uint32(p.From.Reg)
|
|
|
|
case AVNOTV:
|
|
// Set mask bit
|
|
switch {
|
|
case ins.rs1 == obj.REG_NONE:
|
|
ins.funct7 |= 1 // unmasked
|
|
case ins.rs1 != REG_V0:
|
|
p.Ctxt.Diag("%v: invalid vector mask register", p)
|
|
}
|
|
ins.as = AVXORVI
|
|
ins.rd, ins.rs1, ins.rs2, ins.imm = uint32(p.To.Reg), obj.REG_NONE, uint32(p.From.Reg), -1
|
|
|
|
case AVMSGTVV, AVMSGTUVV, AVMSGEVV, AVMSGEUVV, AVMFGTVV, AVMFGEVV:
|
|
// Set mask bit
|
|
switch {
|
|
case ins.rs3 == obj.REG_NONE:
|
|
ins.funct7 |= 1 // unmasked
|
|
case ins.rs3 != REG_V0:
|
|
p.Ctxt.Diag("%v: invalid vector mask register", p)
|
|
}
|
|
switch ins.as {
|
|
case AVMSGTVV:
|
|
ins.as = AVMSLTVV
|
|
case AVMSGTUVV:
|
|
ins.as = AVMSLTUVV
|
|
case AVMSGEVV:
|
|
ins.as = AVMSLEVV
|
|
case AVMSGEUVV:
|
|
ins.as = AVMSLEUVV
|
|
case AVMFGTVV:
|
|
ins.as = AVMFLTVV
|
|
case AVMFGEVV:
|
|
ins.as = AVMFLEVV
|
|
}
|
|
ins.rd, ins.rs1, ins.rs2, ins.rs3 = uint32(p.To.Reg), uint32(p.Reg), uint32(p.From.Reg), obj.REG_NONE
|
|
|
|
case AVMSLTVI, AVMSLTUVI, AVMSGEVI, AVMSGEUVI:
|
|
// Set mask bit
|
|
switch {
|
|
case ins.rs3 == obj.REG_NONE:
|
|
ins.funct7 |= 1 // unmasked
|
|
case ins.rs3 != REG_V0:
|
|
p.Ctxt.Diag("%v: invalid vector mask register", p)
|
|
}
|
|
switch ins.as {
|
|
case AVMSLTVI:
|
|
ins.as = AVMSLEVI
|
|
case AVMSLTUVI:
|
|
ins.as = AVMSLEUVI
|
|
case AVMSGEVI:
|
|
ins.as = AVMSGTVI
|
|
case AVMSGEUVI:
|
|
ins.as = AVMSGTUVI
|
|
}
|
|
ins.rd, ins.rs1, ins.rs2, ins.rs3, ins.imm = uint32(p.To.Reg), obj.REG_NONE, uint32(p.Reg), obj.REG_NONE, ins.imm-1
|
|
|
|
case AVFABSV, AVFNEGV:
|
|
// Set mask bit
|
|
switch {
|
|
case ins.rs1 == obj.REG_NONE:
|
|
ins.funct7 |= 1 // unmasked
|
|
case ins.rs1 != REG_V0:
|
|
p.Ctxt.Diag("%v: invalid vector mask register", p)
|
|
}
|
|
switch ins.as {
|
|
case AVFABSV:
|
|
ins.as = AVFSGNJXVV
|
|
case AVFNEGV:
|
|
ins.as = AVFSGNJNVV
|
|
}
|
|
ins.rd, ins.rs1, ins.rs2 = uint32(p.To.Reg), uint32(p.From.Reg), uint32(p.From.Reg)
|
|
|
|
case AVMANDMM, AVMNANDMM, AVMANDNMM, AVMXORMM, AVMORMM, AVMNORMM, AVMORNMM, AVMXNORMM, AVMMVM, AVMNOTM, AVCOMPRESSVM:
|
|
ins.rd, ins.rs1, ins.rs2 = uint32(p.To.Reg), uint32(p.From.Reg), uint32(p.Reg)
|
|
switch ins.as {
|
|
case AVMMVM:
|
|
ins.as, ins.rs2 = AVMANDMM, ins.rs1
|
|
case AVMNOTM:
|
|
ins.as, ins.rs2 = AVMNANDMM, ins.rs1
|
|
}
|
|
|
|
case AVMCLRM, AVMSETM:
|
|
ins.rd, ins.rs1, ins.rs2 = uint32(p.From.Reg), uint32(p.From.Reg), uint32(p.From.Reg)
|
|
switch ins.as {
|
|
case AVMCLRM:
|
|
ins.as = AVMXORMM
|
|
case AVMSETM:
|
|
ins.as = AVMXNORMM
|
|
}
|
|
|
|
case AVCPOPM, AVFIRSTM, AVMSBFM, AVMSIFM, AVMSOFM, AVIOTAM:
|
|
// Set mask bit
|
|
switch {
|
|
case ins.rs1 == obj.REG_NONE:
|
|
ins.funct7 |= 1 // unmasked
|
|
case ins.rs1 != REG_V0:
|
|
p.Ctxt.Diag("%v: invalid vector mask register", p)
|
|
}
|
|
ins.rs1 = obj.REG_NONE
|
|
|
|
case AVIDV:
|
|
// Set mask bit
|
|
switch {
|
|
case ins.rd == obj.REG_NONE:
|
|
ins.funct7 |= 1 // unmasked
|
|
case ins.rd != obj.REG_NONE && ins.rs2 != REG_V0:
|
|
p.Ctxt.Diag("%v: invalid vector mask register", p)
|
|
}
|
|
if ins.rd == obj.REG_NONE {
|
|
ins.rd = uint32(p.From.Reg)
|
|
}
|
|
ins.rs1, ins.rs2 = obj.REG_NONE, REG_V0
|
|
}
|
|
|
|
// Only compress instructions when there is no relocation, since
|
|
// relocation relies on knowledge about the exact instructions that
|
|
// are in use.
|
|
if compress && p.Mark&NEED_RELOC == 0 {
|
|
for _, ins := range inss {
|
|
ins.compress()
|
|
}
|
|
}
|
|
|
|
for _, ins := range inss {
|
|
ins.p = p
|
|
}
|
|
|
|
return inss
|
|
}
|
|
|
|
// assemble emits machine code.
|
|
// It is called at the very end of the assembly process.
|
|
func assemble(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
|
if ctxt.Retpoline {
|
|
ctxt.Diag("-spectre=ret not supported on riscv")
|
|
ctxt.Retpoline = false // don't keep printing
|
|
}
|
|
|
|
// If errors were encountered during preprocess/validation, proceeding
|
|
// and attempting to encode said instructions will only lead to panics.
|
|
if ctxt.Errors > 0 {
|
|
return
|
|
}
|
|
|
|
for p := cursym.Func().Text; p != nil; p = p.Link {
|
|
switch p.As {
|
|
case AJAL:
|
|
if p.Mark&NEED_JAL_RELOC == NEED_JAL_RELOC {
|
|
cursym.AddRel(ctxt, obj.Reloc{
|
|
Type: objabi.R_RISCV_JAL,
|
|
Off: int32(p.Pc),
|
|
Siz: 4,
|
|
Sym: p.To.Sym,
|
|
Add: p.To.Offset,
|
|
})
|
|
}
|
|
|
|
case ACJALR, AJALR:
|
|
if p.To.Sym != nil {
|
|
ctxt.Diag("%v: unexpected AJALR with to symbol", p)
|
|
}
|
|
|
|
case AAUIPC, AMOV, AMOVB, AMOVH, AMOVW, AMOVBU, AMOVHU, AMOVWU, AMOVF, AMOVD:
|
|
var addr *obj.Addr
|
|
var rt objabi.RelocType
|
|
if p.Mark&NEED_CALL_RELOC == NEED_CALL_RELOC {
|
|
rt = objabi.R_RISCV_CALL
|
|
addr = &p.From
|
|
} else if p.Mark&NEED_PCREL_ITYPE_RELOC == NEED_PCREL_ITYPE_RELOC {
|
|
rt = objabi.R_RISCV_PCREL_ITYPE
|
|
addr = &p.From
|
|
} else if p.Mark&NEED_PCREL_STYPE_RELOC == NEED_PCREL_STYPE_RELOC {
|
|
rt = objabi.R_RISCV_PCREL_STYPE
|
|
addr = &p.To
|
|
} else if p.Mark&NEED_GOT_PCREL_ITYPE_RELOC == NEED_GOT_PCREL_ITYPE_RELOC {
|
|
rt = objabi.R_RISCV_GOT_PCREL_ITYPE
|
|
addr = &p.From
|
|
} else {
|
|
break
|
|
}
|
|
if p.As == AAUIPC {
|
|
if p.Link == nil {
|
|
ctxt.Diag("AUIPC needing PC-relative reloc missing following instruction")
|
|
break
|
|
}
|
|
addr = &p.RestArgs[0].Addr
|
|
}
|
|
if addr.Sym == nil {
|
|
ctxt.Diag("PC-relative relocation missing symbol")
|
|
break
|
|
}
|
|
if addr.Sym.Type == objabi.STLSBSS {
|
|
if ctxt.Flag_shared {
|
|
rt = objabi.R_RISCV_TLS_IE
|
|
} else {
|
|
rt = objabi.R_RISCV_TLS_LE
|
|
}
|
|
}
|
|
|
|
cursym.AddRel(ctxt, obj.Reloc{
|
|
Type: rt,
|
|
Off: int32(p.Pc),
|
|
Siz: 8,
|
|
Sym: addr.Sym,
|
|
Add: addr.Offset,
|
|
})
|
|
|
|
case obj.APCALIGN:
|
|
alignedValue := p.From.Offset
|
|
v := pcAlignPadLength(p.Pc, alignedValue)
|
|
offset := p.Pc
|
|
for ; v >= 4; v -= 4 {
|
|
// NOP (ADDI $0, X0, X0)
|
|
cursym.WriteBytes(ctxt, offset, []byte{0x13, 0x00, 0x00, 0x00})
|
|
offset += 4
|
|
}
|
|
if v == 2 {
|
|
// CNOP
|
|
cursym.WriteBytes(ctxt, offset, []byte{0x01, 0x00})
|
|
offset += 2
|
|
} else if v != 0 {
|
|
ctxt.Diag("bad PCALIGN pad length")
|
|
}
|
|
continue
|
|
}
|
|
|
|
offset := p.Pc
|
|
for _, ins := range instructionsForProg(p, ctxt.CompressInstructions) {
|
|
if ic, err := ins.encode(); err == nil {
|
|
cursym.WriteInt(ctxt, offset, ins.length(), int64(ic))
|
|
offset += int64(ins.length())
|
|
}
|
|
if ins.usesRegTmp() {
|
|
p.Mark |= USES_REG_TMP
|
|
}
|
|
}
|
|
}
|
|
|
|
obj.MarkUnsafePoints(ctxt, cursym.Func().Text, newprog, isUnsafePoint, nil)
|
|
}
|
|
|
|
func isUnsafePoint(p *obj.Prog) bool {
|
|
return p.Mark&USES_REG_TMP == USES_REG_TMP || p.From.Reg == REG_TMP || p.To.Reg == REG_TMP || p.Reg == REG_TMP
|
|
}
|
|
|
|
func ParseSuffix(prog *obj.Prog, cond string) (err error) {
|
|
switch prog.As {
|
|
case AFCVTWS, AFCVTLS, AFCVTWUS, AFCVTLUS, AFCVTWD, AFCVTLD, AFCVTWUD, AFCVTLUD:
|
|
prog.Scond, err = rmSuffixEncode(strings.TrimPrefix(cond, "."))
|
|
}
|
|
return
|
|
}
|
|
|
|
var LinkRISCV64 = obj.LinkArch{
|
|
Arch: sys.ArchRISCV64,
|
|
Init: buildop,
|
|
Preprocess: preprocess,
|
|
Assemble: assemble,
|
|
Progedit: progedit,
|
|
UnaryDst: unaryDst,
|
|
DWARFRegisters: RISCV64DWARFRegisters,
|
|
}
|