blob: 064a1ca111e8c10f75b8ce0840a1f72d38a5d065 [file] [log] [blame]
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package riscv64
import (
"cmd/compile/internal/gc"
"cmd/compile/internal/ssa"
"cmd/compile/internal/types"
"cmd/internal/obj"
"cmd/internal/obj/riscv"
)
// ssaRegToReg maps ssa register numbers to obj register numbers.
var ssaRegToReg = []int16{
riscv.REG_X0,
// X1 (LR): unused
riscv.REG_X2,
riscv.REG_X3,
riscv.REG_X4,
riscv.REG_X5,
riscv.REG_X6,
riscv.REG_X7,
riscv.REG_X8,
riscv.REG_X9,
riscv.REG_X10,
riscv.REG_X11,
riscv.REG_X12,
riscv.REG_X13,
riscv.REG_X14,
riscv.REG_X15,
riscv.REG_X16,
riscv.REG_X17,
riscv.REG_X18,
riscv.REG_X19,
riscv.REG_X20,
riscv.REG_X21,
riscv.REG_X22,
riscv.REG_X23,
riscv.REG_X24,
riscv.REG_X25,
riscv.REG_X26,
riscv.REG_X27,
riscv.REG_X28,
riscv.REG_X29,
riscv.REG_X30,
riscv.REG_X31,
riscv.REG_F0,
riscv.REG_F1,
riscv.REG_F2,
riscv.REG_F3,
riscv.REG_F4,
riscv.REG_F5,
riscv.REG_F6,
riscv.REG_F7,
riscv.REG_F8,
riscv.REG_F9,
riscv.REG_F10,
riscv.REG_F11,
riscv.REG_F12,
riscv.REG_F13,
riscv.REG_F14,
riscv.REG_F15,
riscv.REG_F16,
riscv.REG_F17,
riscv.REG_F18,
riscv.REG_F19,
riscv.REG_F20,
riscv.REG_F21,
riscv.REG_F22,
riscv.REG_F23,
riscv.REG_F24,
riscv.REG_F25,
riscv.REG_F26,
riscv.REG_F27,
riscv.REG_F28,
riscv.REG_F29,
riscv.REG_F30,
riscv.REG_F31,
0, // SB isn't a real register. We fill an Addr.Reg field with 0 in this case.
}
func loadByType(t *types.Type) obj.As {
width := t.Size()
if t.IsFloat() {
switch width {
case 4:
return riscv.AMOVF
case 8:
return riscv.AMOVD
default:
gc.Fatalf("unknown float width for load %d in type %v", width, t)
return 0
}
}
switch width {
case 1:
if t.IsSigned() {
return riscv.AMOVB
} else {
return riscv.AMOVBU
}
case 2:
if t.IsSigned() {
return riscv.AMOVH
} else {
return riscv.AMOVHU
}
case 4:
if t.IsSigned() {
return riscv.AMOVW
} else {
return riscv.AMOVWU
}
case 8:
return riscv.AMOV
default:
gc.Fatalf("unknown width for load %d in type %v", width, t)
return 0
}
}
// storeByType returns the store instruction of the given type.
func storeByType(t *types.Type) obj.As {
width := t.Size()
if t.IsFloat() {
switch width {
case 4:
return riscv.AMOVF
case 8:
return riscv.AMOVD
default:
gc.Fatalf("unknown float width for store %d in type %v", width, t)
return 0
}
}
switch width {
case 1:
return riscv.AMOVB
case 2:
return riscv.AMOVH
case 4:
return riscv.AMOVW
case 8:
return riscv.AMOV
default:
gc.Fatalf("unknown width for store %d in type %v", width, t)
return 0
}
}
// largestMove returns the largest move instruction possible and its size,
// given the alignment of the total size of the move.
//
// e.g., a 16-byte move may use MOV, but an 11-byte move must use MOVB.
//
// Note that the moves may not be on naturally aligned addresses depending on
// the source and destination.
//
// This matches the calculation in ssa.moveSize.
func largestMove(alignment int64) (obj.As, int64) {
switch {
case alignment%8 == 0:
return riscv.AMOV, 8
case alignment%4 == 0:
return riscv.AMOVW, 4
case alignment%2 == 0:
return riscv.AMOVH, 2
default:
return riscv.AMOVB, 1
}
}
// markMoves marks any MOVXconst ops that need to avoid clobbering flags.
// RISC-V has no flags, so this is a no-op.
func ssaMarkMoves(s *gc.SSAGenState, b *ssa.Block) {}
func ssaGenValue(s *gc.SSAGenState, v *ssa.Value) {
s.SetPos(v.Pos)
switch v.Op {
case ssa.OpInitMem:
// memory arg needs no code
case ssa.OpArg:
// input args need no code
case ssa.OpPhi:
gc.CheckLoweredPhi(v)
case ssa.OpCopy, ssa.OpRISCV64MOVconvert, ssa.OpRISCV64MOVDreg:
if v.Type.IsMemory() {
return
}
rs := v.Args[0].Reg()
rd := v.Reg()
if rs == rd {
return
}
as := riscv.AMOV
if v.Type.IsFloat() {
as = riscv.AMOVD
}
p := s.Prog(as)
p.From.Type = obj.TYPE_REG
p.From.Reg = rs
p.To.Type = obj.TYPE_REG
p.To.Reg = rd
case ssa.OpRISCV64MOVDnop:
if v.Reg() != v.Args[0].Reg() {
v.Fatalf("input[0] and output not in same register %s", v.LongString())
}
// nothing to do
case ssa.OpLoadReg:
if v.Type.IsFlags() {
v.Fatalf("load flags not implemented: %v", v.LongString())
return
}
p := s.Prog(loadByType(v.Type))
gc.AddrAuto(&p.From, v.Args[0])
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpStoreReg:
if v.Type.IsFlags() {
v.Fatalf("store flags not implemented: %v", v.LongString())
return
}
p := s.Prog(storeByType(v.Type))
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[0].Reg()
gc.AddrAuto(&p.To, v)
case ssa.OpSP, ssa.OpSB, ssa.OpGetG:
// nothing to do
case ssa.OpRISCV64MOVBreg, ssa.OpRISCV64MOVHreg, ssa.OpRISCV64MOVWreg,
ssa.OpRISCV64MOVBUreg, ssa.OpRISCV64MOVHUreg, ssa.OpRISCV64MOVWUreg:
a := v.Args[0]
for a.Op == ssa.OpCopy || a.Op == ssa.OpRISCV64MOVDreg {
a = a.Args[0]
}
as := v.Op.Asm()
rs := v.Args[0].Reg()
rd := v.Reg()
if a.Op == ssa.OpLoadReg {
t := a.Type
switch {
case v.Op == ssa.OpRISCV64MOVBreg && t.Size() == 1 && t.IsSigned(),
v.Op == ssa.OpRISCV64MOVHreg && t.Size() == 2 && t.IsSigned(),
v.Op == ssa.OpRISCV64MOVWreg && t.Size() == 4 && t.IsSigned(),
v.Op == ssa.OpRISCV64MOVBUreg && t.Size() == 1 && !t.IsSigned(),
v.Op == ssa.OpRISCV64MOVHUreg && t.Size() == 2 && !t.IsSigned(),
v.Op == ssa.OpRISCV64MOVWUreg && t.Size() == 4 && !t.IsSigned():
// arg is a proper-typed load and already sign/zero-extended
if rs == rd {
return
}
as = riscv.AMOV
default:
}
}
p := s.Prog(as)
p.From.Type = obj.TYPE_REG
p.From.Reg = rs
p.To.Type = obj.TYPE_REG
p.To.Reg = rd
case ssa.OpRISCV64ADD, ssa.OpRISCV64SUB, ssa.OpRISCV64SUBW, ssa.OpRISCV64XOR, ssa.OpRISCV64OR, ssa.OpRISCV64AND,
ssa.OpRISCV64SLL, ssa.OpRISCV64SRA, ssa.OpRISCV64SRL,
ssa.OpRISCV64SLT, ssa.OpRISCV64SLTU, ssa.OpRISCV64MUL, ssa.OpRISCV64MULW, ssa.OpRISCV64MULH,
ssa.OpRISCV64MULHU, ssa.OpRISCV64DIV, ssa.OpRISCV64DIVU, ssa.OpRISCV64DIVW,
ssa.OpRISCV64DIVUW, ssa.OpRISCV64REM, ssa.OpRISCV64REMU, ssa.OpRISCV64REMW,
ssa.OpRISCV64REMUW,
ssa.OpRISCV64FADDS, ssa.OpRISCV64FSUBS, ssa.OpRISCV64FMULS, ssa.OpRISCV64FDIVS,
ssa.OpRISCV64FEQS, ssa.OpRISCV64FNES, ssa.OpRISCV64FLTS, ssa.OpRISCV64FLES,
ssa.OpRISCV64FADDD, ssa.OpRISCV64FSUBD, ssa.OpRISCV64FMULD, ssa.OpRISCV64FDIVD,
ssa.OpRISCV64FEQD, ssa.OpRISCV64FNED, ssa.OpRISCV64FLTD, ssa.OpRISCV64FLED:
r := v.Reg()
r1 := v.Args[0].Reg()
r2 := v.Args[1].Reg()
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = r2
p.Reg = r1
p.To.Type = obj.TYPE_REG
p.To.Reg = r
case ssa.OpRISCV64FSQRTS, ssa.OpRISCV64FNEGS, ssa.OpRISCV64FSQRTD, ssa.OpRISCV64FNEGD,
ssa.OpRISCV64FMVSX, ssa.OpRISCV64FMVDX,
ssa.OpRISCV64FCVTSW, ssa.OpRISCV64FCVTSL, ssa.OpRISCV64FCVTWS, ssa.OpRISCV64FCVTLS,
ssa.OpRISCV64FCVTDW, ssa.OpRISCV64FCVTDL, ssa.OpRISCV64FCVTWD, ssa.OpRISCV64FCVTLD, ssa.OpRISCV64FCVTDS, ssa.OpRISCV64FCVTSD,
ssa.OpRISCV64NOT, ssa.OpRISCV64NEG, ssa.OpRISCV64NEGW:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[0].Reg()
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpRISCV64ADDI, ssa.OpRISCV64ADDIW, ssa.OpRISCV64XORI, ssa.OpRISCV64ORI, ssa.OpRISCV64ANDI,
ssa.OpRISCV64SLLI, ssa.OpRISCV64SRAI, ssa.OpRISCV64SRLI, ssa.OpRISCV64SLTI,
ssa.OpRISCV64SLTIU:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_CONST
p.From.Offset = v.AuxInt
p.Reg = v.Args[0].Reg()
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpRISCV64MOVBconst, ssa.OpRISCV64MOVHconst, ssa.OpRISCV64MOVWconst, ssa.OpRISCV64MOVDconst:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_CONST
p.From.Offset = v.AuxInt
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpRISCV64MOVaddr:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_ADDR
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
var wantreg string
// MOVW $sym+off(base), R
switch v.Aux.(type) {
default:
v.Fatalf("aux is of unknown type %T", v.Aux)
case *obj.LSym:
wantreg = "SB"
gc.AddAux(&p.From, v)
case *gc.Node:
wantreg = "SP"
gc.AddAux(&p.From, v)
case nil:
// No sym, just MOVW $off(SP), R
wantreg = "SP"
p.From.Reg = riscv.REG_SP
p.From.Offset = v.AuxInt
}
if reg := v.Args[0].RegName(); reg != wantreg {
v.Fatalf("bad reg %s for symbol type %T, want %s", reg, v.Aux, wantreg)
}
case ssa.OpRISCV64MOVBload, ssa.OpRISCV64MOVHload, ssa.OpRISCV64MOVWload, ssa.OpRISCV64MOVDload,
ssa.OpRISCV64MOVBUload, ssa.OpRISCV64MOVHUload, ssa.OpRISCV64MOVWUload,
ssa.OpRISCV64FMOVWload, ssa.OpRISCV64FMOVDload:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
gc.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpRISCV64MOVBstore, ssa.OpRISCV64MOVHstore, ssa.OpRISCV64MOVWstore, ssa.OpRISCV64MOVDstore,
ssa.OpRISCV64FMOVWstore, ssa.OpRISCV64FMOVDstore:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[1].Reg()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
gc.AddAux(&p.To, v)
case ssa.OpRISCV64MOVBstorezero, ssa.OpRISCV64MOVHstorezero, ssa.OpRISCV64MOVWstorezero, ssa.OpRISCV64MOVDstorezero:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = riscv.REG_ZERO
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
gc.AddAux(&p.To, v)
case ssa.OpRISCV64SEQZ, ssa.OpRISCV64SNEZ:
p := s.Prog(v.Op.Asm())
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[0].Reg()
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpRISCV64CALLstatic, ssa.OpRISCV64CALLclosure, ssa.OpRISCV64CALLinter:
s.Call(v)
case ssa.OpRISCV64LoweredWB:
p := s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
p.To.Sym = v.Aux.(*obj.LSym)
case ssa.OpRISCV64LoweredPanicBoundsA, ssa.OpRISCV64LoweredPanicBoundsB, ssa.OpRISCV64LoweredPanicBoundsC:
p := s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
p.To.Sym = gc.BoundsCheckFunc[v.AuxInt]
s.UseArgs(16) // space used in callee args area by assembly stubs
case ssa.OpRISCV64LoweredAtomicLoad8:
s.Prog(riscv.AFENCE)
p := s.Prog(riscv.AMOVBU)
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg0()
s.Prog(riscv.AFENCE)
case ssa.OpRISCV64LoweredAtomicLoad32, ssa.OpRISCV64LoweredAtomicLoad64:
as := riscv.ALRW
if v.Op == ssa.OpRISCV64LoweredAtomicLoad64 {
as = riscv.ALRD
}
p := s.Prog(as)
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg0()
case ssa.OpRISCV64LoweredAtomicStore8:
s.Prog(riscv.AFENCE)
p := s.Prog(riscv.AMOVB)
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[1].Reg()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
s.Prog(riscv.AFENCE)
case ssa.OpRISCV64LoweredAtomicStore32, ssa.OpRISCV64LoweredAtomicStore64:
as := riscv.AAMOSWAPW
if v.Op == ssa.OpRISCV64LoweredAtomicStore64 {
as = riscv.AAMOSWAPD
}
p := s.Prog(as)
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[1].Reg()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
p.RegTo2 = riscv.REG_ZERO
case ssa.OpRISCV64LoweredAtomicAdd32, ssa.OpRISCV64LoweredAtomicAdd64:
as := riscv.AAMOADDW
if v.Op == ssa.OpRISCV64LoweredAtomicAdd64 {
as = riscv.AAMOADDD
}
p := s.Prog(as)
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[1].Reg()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
p.RegTo2 = riscv.REG_TMP
p2 := s.Prog(riscv.AADD)
p2.From.Type = obj.TYPE_REG
p2.From.Reg = riscv.REG_TMP
p2.Reg = v.Args[1].Reg()
p2.To.Type = obj.TYPE_REG
p2.To.Reg = v.Reg0()
case ssa.OpRISCV64LoweredAtomicExchange32, ssa.OpRISCV64LoweredAtomicExchange64:
as := riscv.AAMOSWAPW
if v.Op == ssa.OpRISCV64LoweredAtomicExchange64 {
as = riscv.AAMOSWAPD
}
p := s.Prog(as)
p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[1].Reg()
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
p.RegTo2 = v.Reg0()
case ssa.OpRISCV64LoweredAtomicCas32, ssa.OpRISCV64LoweredAtomicCas64:
// MOV ZERO, Rout
// LR (Rarg0), Rtmp
// BNE Rtmp, Rarg1, 3(PC)
// SC Rarg2, (Rarg0), Rtmp
// BNE Rtmp, ZERO, -3(PC)
// MOV $1, Rout
lr := riscv.ALRW
sc := riscv.ASCW
if v.Op == ssa.OpRISCV64LoweredAtomicCas64 {
lr = riscv.ALRD
sc = riscv.ASCD
}
r0 := v.Args[0].Reg()
r1 := v.Args[1].Reg()
r2 := v.Args[2].Reg()
out := v.Reg0()
p := s.Prog(riscv.AMOV)
p.From.Type = obj.TYPE_REG
p.From.Reg = riscv.REG_ZERO
p.To.Type = obj.TYPE_REG
p.To.Reg = out
p1 := s.Prog(lr)
p1.From.Type = obj.TYPE_MEM
p1.From.Reg = r0
p1.To.Type = obj.TYPE_REG
p1.To.Reg = riscv.REG_TMP
p2 := s.Prog(riscv.ABNE)
p2.From.Type = obj.TYPE_REG
p2.From.Reg = r1
p2.Reg = riscv.REG_TMP
p2.To.Type = obj.TYPE_BRANCH
p3 := s.Prog(sc)
p3.From.Type = obj.TYPE_REG
p3.From.Reg = r2
p3.To.Type = obj.TYPE_MEM
p3.To.Reg = r0
p3.RegTo2 = riscv.REG_TMP
p4 := s.Prog(riscv.ABNE)
p4.From.Type = obj.TYPE_REG
p4.From.Reg = riscv.REG_TMP
p4.Reg = riscv.REG_ZERO
p4.To.Type = obj.TYPE_BRANCH
gc.Patch(p4, p1)
p5 := s.Prog(riscv.AMOV)
p5.From.Type = obj.TYPE_CONST
p5.From.Offset = 1
p5.To.Type = obj.TYPE_REG
p5.To.Reg = out
p6 := s.Prog(obj.ANOP)
gc.Patch(p2, p6)
case ssa.OpRISCV64LoweredZero:
mov, sz := largestMove(v.AuxInt)
// mov ZERO, (Rarg0)
// ADD $sz, Rarg0
// BGEU Rarg1, Rarg0, -2(PC)
p := s.Prog(mov)
p.From.Type = obj.TYPE_REG
p.From.Reg = riscv.REG_ZERO
p.To.Type = obj.TYPE_MEM
p.To.Reg = v.Args[0].Reg()
p2 := s.Prog(riscv.AADD)
p2.From.Type = obj.TYPE_CONST
p2.From.Offset = sz
p2.To.Type = obj.TYPE_REG
p2.To.Reg = v.Args[0].Reg()
p3 := s.Prog(riscv.ABGEU)
p3.To.Type = obj.TYPE_BRANCH
p3.Reg = v.Args[0].Reg()
p3.From.Type = obj.TYPE_REG
p3.From.Reg = v.Args[1].Reg()
gc.Patch(p3, p)
case ssa.OpRISCV64LoweredMove:
mov, sz := largestMove(v.AuxInt)
// mov (Rarg1), T2
// mov T2, (Rarg0)
// ADD $sz, Rarg0
// ADD $sz, Rarg1
// BGEU Rarg2, Rarg0, -4(PC)
p := s.Prog(mov)
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[1].Reg()
p.To.Type = obj.TYPE_REG
p.To.Reg = riscv.REG_T2
p2 := s.Prog(mov)
p2.From.Type = obj.TYPE_REG
p2.From.Reg = riscv.REG_T2
p2.To.Type = obj.TYPE_MEM
p2.To.Reg = v.Args[0].Reg()
p3 := s.Prog(riscv.AADD)
p3.From.Type = obj.TYPE_CONST
p3.From.Offset = sz
p3.To.Type = obj.TYPE_REG
p3.To.Reg = v.Args[0].Reg()
p4 := s.Prog(riscv.AADD)
p4.From.Type = obj.TYPE_CONST
p4.From.Offset = sz
p4.To.Type = obj.TYPE_REG
p4.To.Reg = v.Args[1].Reg()
p5 := s.Prog(riscv.ABGEU)
p5.To.Type = obj.TYPE_BRANCH
p5.Reg = v.Args[1].Reg()
p5.From.Type = obj.TYPE_REG
p5.From.Reg = v.Args[2].Reg()
gc.Patch(p5, p)
case ssa.OpRISCV64LoweredNilCheck:
// Issue a load which will fault if arg is nil.
// TODO: optimizations. See arm and amd64 LoweredNilCheck.
p := s.Prog(riscv.AMOVB)
p.From.Type = obj.TYPE_MEM
p.From.Reg = v.Args[0].Reg()
gc.AddAux(&p.From, v)
p.To.Type = obj.TYPE_REG
p.To.Reg = riscv.REG_ZERO
if gc.Debug_checknil != 0 && v.Pos.Line() > 1 { // v.Pos == 1 in generated wrappers
gc.Warnl(v.Pos, "generated nil check")
}
case ssa.OpRISCV64LoweredGetClosurePtr:
// Closure pointer is S4 (riscv.REG_CTXT).
gc.CheckLoweredGetClosurePtr(v)
case ssa.OpRISCV64LoweredGetCallerSP:
// caller's SP is FixedFrameSize below the address of the first arg
p := s.Prog(riscv.AMOV)
p.From.Type = obj.TYPE_ADDR
p.From.Offset = -gc.Ctxt.FixedFrameSize()
p.From.Name = obj.NAME_PARAM
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
case ssa.OpRISCV64LoweredGetCallerPC:
p := s.Prog(obj.AGETCALLERPC)
p.To.Type = obj.TYPE_REG
p.To.Reg = v.Reg()
default:
v.Fatalf("Unhandled op %v", v.Op)
}
}
var blockBranch = [...]obj.As{
ssa.BlockRISCV64BEQ: riscv.ABEQ,
ssa.BlockRISCV64BEQZ: riscv.ABEQZ,
ssa.BlockRISCV64BGE: riscv.ABGE,
ssa.BlockRISCV64BGEU: riscv.ABGEU,
ssa.BlockRISCV64BGEZ: riscv.ABGEZ,
ssa.BlockRISCV64BGTZ: riscv.ABGTZ,
ssa.BlockRISCV64BLEZ: riscv.ABLEZ,
ssa.BlockRISCV64BLT: riscv.ABLT,
ssa.BlockRISCV64BLTU: riscv.ABLTU,
ssa.BlockRISCV64BLTZ: riscv.ABLTZ,
ssa.BlockRISCV64BNE: riscv.ABNE,
ssa.BlockRISCV64BNEZ: riscv.ABNEZ,
}
func ssaGenBlock(s *gc.SSAGenState, b, next *ssa.Block) {
s.SetPos(b.Pos)
switch b.Kind {
case ssa.BlockDefer:
// defer returns in A0:
// 0 if we should continue executing
// 1 if we should jump to deferreturn call
p := s.Prog(riscv.ABNE)
p.To.Type = obj.TYPE_BRANCH
p.From.Type = obj.TYPE_REG
p.From.Reg = riscv.REG_ZERO
p.Reg = riscv.REG_A0
s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[1].Block()})
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
}
case ssa.BlockPlain:
if b.Succs[0].Block() != next {
p := s.Prog(obj.AJMP)
p.To.Type = obj.TYPE_BRANCH
s.Branches = append(s.Branches, gc.Branch{P: p, B: b.Succs[0].Block()})
}
case ssa.BlockExit:
case ssa.BlockRet:
s.Prog(obj.ARET)
case ssa.BlockRetJmp:
p := s.Prog(obj.ARET)
p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN
p.To.Sym = b.Aux.(*obj.LSym)
case ssa.BlockRISCV64BEQ, ssa.BlockRISCV64BEQZ, ssa.BlockRISCV64BNE, ssa.BlockRISCV64BNEZ,
ssa.BlockRISCV64BLT, ssa.BlockRISCV64BLEZ, ssa.BlockRISCV64BGE, ssa.BlockRISCV64BGEZ,
ssa.BlockRISCV64BLTZ, ssa.BlockRISCV64BGTZ, ssa.BlockRISCV64BLTU, ssa.BlockRISCV64BGEU:
as := blockBranch[b.Kind]
invAs := riscv.InvertBranch(as)
var p *obj.Prog
switch next {
case b.Succs[0].Block():
p = s.Br(invAs, b.Succs[1].Block())
case b.Succs[1].Block():
p = s.Br(as, b.Succs[0].Block())
default:
if b.Likely != ssa.BranchUnlikely {
p = s.Br(as, b.Succs[0].Block())
s.Br(obj.AJMP, b.Succs[1].Block())
} else {
p = s.Br(invAs, b.Succs[1].Block())
s.Br(obj.AJMP, b.Succs[0].Block())
}
}
p.From.Type = obj.TYPE_REG
switch b.Kind {
case ssa.BlockRISCV64BEQ, ssa.BlockRISCV64BNE, ssa.BlockRISCV64BLT, ssa.BlockRISCV64BGE, ssa.BlockRISCV64BLTU, ssa.BlockRISCV64BGEU:
if b.NumControls() != 2 {
b.Fatalf("Unexpected number of controls (%d != 2): %s", b.NumControls(), b.LongString())
}
p.From.Reg = b.Controls[0].Reg()
p.Reg = b.Controls[1].Reg()
case ssa.BlockRISCV64BEQZ, ssa.BlockRISCV64BNEZ, ssa.BlockRISCV64BGEZ, ssa.BlockRISCV64BLEZ, ssa.BlockRISCV64BLTZ, ssa.BlockRISCV64BGTZ:
if b.NumControls() != 1 {
b.Fatalf("Unexpected number of controls (%d != 1): %s", b.NumControls(), b.LongString())
}
p.From.Reg = b.Controls[0].Reg()
}
default:
b.Fatalf("Unhandled block: %s", b.LongString())
}
}