cmd/internal/obj{,/loong64}: instructions and registers for loong64

Implemented an assembler for LoongArch64(loong64 is short name) -
this provides register definitions and instruction encoding as
defined in the LoongArch Instruction Set Manual.

LoongArch Instruction Set Manual:
  https://github.com/loongson/LoongArch-Documentation/releases

Contributors to the linux/loong64 port are:
  Weining Lu <luweining@loongson.cn>
  Lei Wang <wanglei@loongson.cn>
  Lingqin Gong <gonglingqin@loongson.cn>
  Xiaolin Zhao <zhaoxiaolin@loongson.cn>
  Meidan Li <limeidan@loongson.cn>
  Xiaojuan Zhai <zhaixiaojuan@loongson.cn>
  Qiyuan Pu <puqiyuan@loongson.cn>
  Guoqi Chen <chenguoqi@loongson.cn>

This port has been updated to Go 1.15.6:
  https://github.com/loongson/go

Updates #46229

Change-Id: I930d2a19246496e3ca36d55539183c0f9f650ad9
Reviewed-on: https://go-review.googlesource.com/c/go/+/342309
Reviewed-by: David Chase <drchase@google.com>
Auto-Submit: Ian Lance Taylor <iant@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@google.com>
Run-TryBot: Ian Lance Taylor <iant@google.com>
This commit is contained in:
Xiaodong Liu 2021-08-15 16:01:31 +08:00 committed by Gopher Robot
parent 857cda4625
commit c1105cfd43
8 changed files with 3305 additions and 9 deletions

View File

@ -442,6 +442,7 @@ const (
ABasePPC64 ABasePPC64
ABaseARM64 ABaseARM64
ABaseMIPS ABaseMIPS
ABaseLoong64
ABaseRISCV ABaseRISCV
ABaseS390X ABaseS390X
ABaseWasm ABaseWasm

View File

@ -0,0 +1,414 @@
// Copyright 2022 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 loong64
import (
"cmd/internal/obj"
)
//go:generate go run ../stringer.go -i $GOFILE -o anames.go -p loong64
const (
NSNAME = 8
NSYM = 50
NREG = 32 // number of general registers
NFREG = 32 // number of floating point registers
)
const (
REG_R0 = obj.RBaseLOONG64 + iota // must be a multiple of 32
REG_R1
REG_R2
REG_R3
REG_R4
REG_R5
REG_R6
REG_R7
REG_R8
REG_R9
REG_R10
REG_R11
REG_R12
REG_R13
REG_R14
REG_R15
REG_R16
REG_R17
REG_R18
REG_R19
REG_R20
REG_R21
REG_R22
REG_R23
REG_R24
REG_R25
REG_R26
REG_R27
REG_R28
REG_R29
REG_R30
REG_R31
REG_F0 // must be a multiple of 32
REG_F1
REG_F2
REG_F3
REG_F4
REG_F5
REG_F6
REG_F7
REG_F8
REG_F9
REG_F10
REG_F11
REG_F12
REG_F13
REG_F14
REG_F15
REG_F16
REG_F17
REG_F18
REG_F19
REG_F20
REG_F21
REG_F22
REG_F23
REG_F24
REG_F25
REG_F26
REG_F27
REG_F28
REG_F29
REG_F30
REG_F31
REG_FCSR0 // must be a multiple of 32
REG_FCSR1
REG_FCSR2
REG_FCSR3 // only four registers are needed
REG_FCSR4
REG_FCSR5
REG_FCSR6
REG_FCSR7
REG_FCSR8
REG_FCSR9
REG_FCSR10
REG_FCSR11
REG_FCSR12
REG_FCSR13
REG_FCSR14
REG_FCSR15
REG_FCSR16
REG_FCSR17
REG_FCSR18
REG_FCSR19
REG_FCSR20
REG_FCSR21
REG_FCSR22
REG_FCSR23
REG_FCSR24
REG_FCSR25
REG_FCSR26
REG_FCSR27
REG_FCSR28
REG_FCSR29
REG_FCSR30
REG_FCSR31
REG_FCC0 // must be a multiple of 32
REG_FCC1
REG_FCC2
REG_FCC3
REG_FCC4
REG_FCC5
REG_FCC6
REG_FCC7 // only eight registers are needed
REG_FCC8
REG_FCC9
REG_FCC10
REG_FCC11
REG_FCC12
REG_FCC13
REG_FCC14
REG_FCC15
REG_FCC16
REG_FCC17
REG_FCC18
REG_FCC19
REG_FCC20
REG_FCC21
REG_FCC22
REG_FCC23
REG_FCC24
REG_FCC25
REG_FCC26
REG_FCC27
REG_FCC28
REG_FCC29
REG_FCC30
REG_FCC31
REG_LAST = REG_FCC31 // the last defined register
REG_SPECIAL = REG_FCSR0
REGZERO = REG_R0 // set to zero
REGLINK = REG_R1
REGSP = REG_R3
REGRET = REG_R19
REGARG = -1 // -1 disables passing the first argument in register
REGRT1 = REG_R19 // reserved for runtime, duffzero and duffcopy
REGRT2 = REG_R20 // reserved for runtime, duffcopy
REGCTXT = REG_R29 // context for closures
REGG = REG_R22 // G in loong64
REGTMP = REG_R30 // used by the assembler
FREGRET = REG_F0
)
var LOONG64DWARFRegisters = map[int16]int16{}
func init() {
// f assigns dwarfregisters[from:to] = (base):(to-from+base)
f := func(from, to, base int16) {
for r := int16(from); r <= to; r++ {
LOONG64DWARFRegisters[r] = (r - from) + base
}
}
f(REG_R0, REG_R31, 0)
f(REG_F0, REG_F31, 32)
}
const (
BIG = 2046
)
const (
// mark flags
LABEL = 1 << 0
LEAF = 1 << 1
SYNC = 1 << 2
BRANCH = 1 << 3
)
const (
C_NONE = iota
C_REG
C_FREG
C_FCSRREG
C_FCCREG
C_ZCON
C_SCON // 12 bit signed
C_UCON // 32 bit signed, low 12 bits 0
C_ADD0CON
C_AND0CON
C_ADDCON // -0x800 <= v < 0
C_ANDCON // 0 < v <= 0xFFF
C_LCON // other 32
C_DCON // other 64 (could subdivide further)
C_SACON // $n(REG) where n <= int12
C_SECON
C_LACON // $n(REG) where int12 < n <= int32
C_LECON
C_DACON // $n(REG) where int32 < n
C_STCON // $tlsvar
C_SBRA
C_LBRA
C_SAUTO
C_LAUTO
C_SEXT
C_LEXT
C_ZOREG
C_SOREG
C_LOREG
C_GOK
C_ADDR
C_TLS
C_TEXTSIZE
C_NCLASS // must be the last
)
const (
AABSD = obj.ABaseLoong64 + obj.A_ARCHSPECIFIC + iota
AABSF
AADD
AADDD
AADDF
AADDU
AADDW
AAND
ABEQ
ABGEZ
ABLEZ
ABGTZ
ABLTZ
ABFPF
ABFPT
ABNE
ABREAK
ACLO
ACLZ
ACMPEQD
ACMPEQF
ACMPGED // ACMPGED -> fcmp.sle.d
ACMPGEF // ACMPGEF -> fcmp.sle.s
ACMPGTD // ACMPGTD -> fcmp.slt.d
ACMPGTF // ACMPGTF -> fcmp.slt.s
ALU12IW
ALU32ID
ALU52ID
APCADDU12I
AJIRL
ABGE
ABLT
ABLTU
ABGEU
ADIV
ADIVD
ADIVF
ADIVU
ADIVW
ALL
ALLV
ALUI
AMOVB
AMOVBU
AMOVD
AMOVDF
AMOVDW
AMOVF
AMOVFD
AMOVFW
AMOVH
AMOVHU
AMOVW
AMOVWD
AMOVWF
AMOVWL
AMOVWR
AMUL
AMULD
AMULF
AMULU
AMULH
AMULHU
AMULW
ANEGD
ANEGF
ANEGW
ANEGV
ANOOP // hardware nop
ANOR
AOR
AREM
AREMU
ARFE
ASC
ASCV
ASGT
ASGTU
ASLL
ASQRTD
ASQRTF
ASRA
ASRL
ASUB
ASUBD
ASUBF
ASUBU
ASUBW
ADBAR
ASYSCALL
ATEQ
ATNE
AWORD
AXOR
// 64-bit
AMOVV
AMOVVL
AMOVVR
ASLLV
ASRAV
ASRLV
ADIVV
ADIVVU
AREMV
AREMVU
AMULV
AMULVU
AMULHV
AMULHVU
AADDV
AADDVU
ASUBV
ASUBVU
// 64-bit FP
ATRUNCFV
ATRUNCDV
ATRUNCFW
ATRUNCDW
AMOVWU
AMOVFV
AMOVDV
AMOVVF
AMOVVD
ALAST
// aliases
AJMP = obj.AJMP
AJAL = obj.ACALL
ARET = obj.ARET
)
func init() {
// The asm encoder generally assumes that the lowest 5 bits of the
// REG_XX constants match the machine instruction encoding, i.e.
// the lowest 5 bits is the register number.
// Check this here.
if REG_R0%32 != 0 {
panic("REG_R0 is not a multiple of 32")
}
if REG_F0%32 != 0 {
panic("REG_F0 is not a multiple of 32")
}
if REG_FCSR0%32 != 0 {
panic("REG_FCSR0 is not a multiple of 32")
}
if REG_FCC0%32 != 0 {
panic("REG_FCC0 is not a multiple of 32")
}
}

View File

@ -0,0 +1,130 @@
// Code generated by stringer -i a.out.go -o anames.go -p loong64; DO NOT EDIT.
package loong64
import "cmd/internal/obj"
var Anames = []string{
obj.A_ARCHSPECIFIC: "ABSD",
"ABSF",
"ADD",
"ADDD",
"ADDF",
"ADDU",
"ADDW",
"AND",
"BEQ",
"BGEZ",
"BLEZ",
"BGTZ",
"BLTZ",
"BFPF",
"BFPT",
"BNE",
"BREAK",
"CLO",
"CLZ",
"CMPEQD",
"CMPEQF",
"CMPGED",
"CMPGEF",
"CMPGTD",
"CMPGTF",
"LU12IW",
"LU32ID",
"LU52ID",
"PCADDU12I",
"JIRL",
"BGE",
"BLT",
"BLTU",
"BGEU",
"DIV",
"DIVD",
"DIVF",
"DIVU",
"DIVW",
"LL",
"LLV",
"LUI",
"MOVB",
"MOVBU",
"MOVD",
"MOVDF",
"MOVDW",
"MOVF",
"MOVFD",
"MOVFW",
"MOVH",
"MOVHU",
"MOVW",
"MOVWD",
"MOVWF",
"MOVWL",
"MOVWR",
"MUL",
"MULD",
"MULF",
"MULU",
"MULH",
"MULHU",
"MULW",
"NEGD",
"NEGF",
"NEGW",
"NEGV",
"NOOP",
"NOR",
"OR",
"REM",
"REMU",
"RFE",
"SC",
"SCV",
"SGT",
"SGTU",
"SLL",
"SQRTD",
"SQRTF",
"SRA",
"SRL",
"SUB",
"SUBD",
"SUBF",
"SUBU",
"SUBW",
"DBAR",
"SYSCALL",
"TEQ",
"TNE",
"WORD",
"XOR",
"MOVV",
"MOVVL",
"MOVVR",
"SLLV",
"SRAV",
"SRLV",
"DIVV",
"DIVVU",
"REMV",
"REMVU",
"MULV",
"MULVU",
"MULHV",
"MULHVU",
"ADDV",
"ADDVU",
"SUBV",
"SUBVU",
"TRUNCFV",
"TRUNCDV",
"TRUNCFW",
"TRUNCDW",
"MOVWU",
"MOVFV",
"MOVDV",
"MOVVF",
"MOVVD",
"LAST",
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,43 @@
// Copyright 2022 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 loong64
var cnames0 = []string{
"NONE",
"REG",
"FREG",
"FCREG",
"FCSRREG",
"FCCREG",
"ZCON",
"SCON",
"UCON",
"ADD0CON",
"AND0CON",
"ADDCON",
"ANDCON",
"LCON",
"DCON",
"SACON",
"SECON",
"LACON",
"LECON",
"DACON",
"STCON",
"SBRA",
"LBRA",
"SAUTO",
"LAUTO",
"SEXT",
"LEXT",
"ZOREG",
"SOREG",
"LOREG",
"GOK",
"ADDR",
"TLS",
"TEXTSIZE",
"NCLASS",
}

View File

@ -0,0 +1,46 @@
// Copyright 2022 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 loong64
import (
"cmd/internal/obj"
"fmt"
)
func init() {
obj.RegisterRegister(obj.RBaseLOONG64, REG_LAST+1, rconv)
obj.RegisterOpcode(obj.ABaseLoong64, Anames)
}
func rconv(r int) string {
if r == 0 {
return "NONE"
}
if r == REGG {
// Special case.
return "g"
}
if REG_R0 <= r && r <= REG_R31 {
return fmt.Sprintf("R%d", r-REG_R0)
}
if REG_F0 <= r && r <= REG_F31 {
return fmt.Sprintf("F%d", r-REG_F0)
}
if REG_FCSR0 <= r && r <= REG_FCSR31 {
return fmt.Sprintf("FCSR%d", r-REG_FCSR0)
}
if REG_FCC0 <= r && r <= REG_FCC31 {
return fmt.Sprintf("FCC%d", r-REG_FCC0)
}
return fmt.Sprintf("Rgok(%d)", r-obj.RBaseLOONG64)
}
func DRconv(a int) string {
s := "C_??"
if a >= C_NONE && a <= C_NCLASS {
s = cnames0[a]
}
return s
}

View File

@ -0,0 +1,701 @@
// Copyright 2022 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 loong64
import (
"cmd/internal/obj"
"cmd/internal/objabi"
"cmd/internal/sys"
"math"
)
func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
// Rewrite JMP/JAL to symbol as TYPE_BRANCH.
switch p.As {
case AJMP,
AJAL,
ARET,
obj.ADUFFZERO,
obj.ADUFFCOPY:
if p.To.Sym != nil {
p.To.Type = obj.TYPE_BRANCH
}
}
// Rewrite float constants to values stored in memory.
switch p.As {
case AMOVF:
if p.From.Type == obj.TYPE_FCONST {
f32 := float32(p.From.Val.(float64))
if math.Float32bits(f32) == 0 {
p.As = AMOVW
p.From.Type = obj.TYPE_REG
p.From.Reg = REGZERO
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 {
f64 := p.From.Val.(float64)
if math.Float64bits(f64) == 0 {
p.As = AMOVV
p.From.Type = obj.TYPE_REG
p.From.Reg = REGZERO
break
}
p.From.Type = obj.TYPE_MEM
p.From.Sym = ctxt.Float64Sym(f64)
p.From.Name = obj.NAME_EXTERN
p.From.Offset = 0
}
}
// Rewrite SUB constants into ADD.
switch p.As {
case ASUB:
if p.From.Type == obj.TYPE_CONST {
p.From.Offset = -p.From.Offset
p.As = AADD
}
case ASUBU:
if p.From.Type == obj.TYPE_CONST {
p.From.Offset = -p.From.Offset
p.As = AADDU
}
case ASUBV:
if p.From.Type == obj.TYPE_CONST {
p.From.Offset = -p.From.Offset
p.As = AADDV
}
case ASUBVU:
if p.From.Type == obj.TYPE_CONST {
p.From.Offset = -p.From.Offset
p.As = AADDVU
}
}
}
func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
c := ctxt0{ctxt: ctxt, newprog: newprog, cursym: cursym}
p := c.cursym.Func().Text
textstksiz := p.To.Offset
if textstksiz < 0 {
c.ctxt.Diag("negative frame size %d - did you mean NOFRAME?", textstksiz)
}
if p.From.Sym.NoFrame() {
if textstksiz != 0 {
c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
}
}
c.cursym.Func().Args = p.To.Val.(int32)
c.cursym.Func().Locals = int32(textstksiz)
/*
* find leaf subroutines
* expand RET
*/
for p := c.cursym.Func().Text; p != nil; p = p.Link {
switch p.As {
case obj.ATEXT:
p.Mark |= LABEL | LEAF | SYNC
if p.Link != nil {
p.Link.Mark |= LABEL
}
case AMOVW,
AMOVV:
if p.To.Type == obj.TYPE_REG && p.To.Reg >= REG_SPECIAL {
p.Mark |= LABEL | SYNC
break
}
if p.From.Type == obj.TYPE_REG && p.From.Reg >= REG_SPECIAL {
p.Mark |= LABEL | SYNC
}
case ASYSCALL,
AWORD:
p.Mark |= LABEL | SYNC
case ANOR:
if p.To.Type == obj.TYPE_REG {
if p.To.Reg == REGZERO {
p.Mark |= LABEL | SYNC
}
}
case AJAL,
obj.ADUFFZERO,
obj.ADUFFCOPY:
c.cursym.Func().Text.Mark &^= LEAF
fallthrough
case AJMP,
ABEQ,
ABGEU,
ABLTU,
ABLTZ,
ABNE,
ABFPT, ABFPF:
p.Mark |= BRANCH
q1 := p.To.Target()
if q1 != nil {
for q1.As == obj.ANOP {
q1 = q1.Link
p.To.SetTarget(q1)
}
if q1.Mark&LEAF == 0 {
q1.Mark |= LABEL
}
}
q1 = p.Link
if q1 != nil {
q1.Mark |= LABEL
}
case ARET:
if p.Link != nil {
p.Link.Mark |= LABEL
}
}
}
var mov, add obj.As
add = AADDV
mov = AMOVV
var q *obj.Prog
var q1 *obj.Prog
autosize := int32(0)
var p1 *obj.Prog
var p2 *obj.Prog
for p := c.cursym.Func().Text; p != nil; p = p.Link {
o := p.As
switch o {
case obj.ATEXT:
autosize = int32(textstksiz)
if p.Mark&LEAF != 0 && autosize == 0 {
// A leaf function with no locals has no frame.
p.From.Sym.Set(obj.AttrNoFrame, true)
}
if !p.From.Sym.NoFrame() {
// If there is a stack frame at all, it includes
// space to save the LR.
autosize += int32(c.ctxt.Arch.FixedFrameSize)
}
if autosize&4 != 0 {
autosize += 4
}
if autosize == 0 && c.cursym.Func().Text.Mark&LEAF == 0 {
if c.cursym.Func().Text.From.Sym.NoSplit() {
if ctxt.Debugvlog {
ctxt.Logf("save suppressed in: %s\n", c.cursym.Name)
}
c.cursym.Func().Text.Mark |= LEAF
}
}
p.To.Offset = int64(autosize) - ctxt.Arch.FixedFrameSize
if c.cursym.Func().Text.Mark&LEAF != 0 {
c.cursym.Set(obj.AttrLeaf, true)
if p.From.Sym.NoFrame() {
break
}
}
if !p.From.Sym.NoSplit() {
p = c.stacksplit(p, autosize) // emit split check
}
q = p
if autosize != 0 {
// Make sure to save link register for non-empty frame, even if
// it is a leaf function, so that traceback works.
// Store link register before decrement SP, so if a signal comes
// during the execution of the function prologue, the traceback
// code will not see a half-updated stack frame.
// This sequence is not async preemptible, as if we open a frame
// at the current SP, it will clobber the saved LR.
q = c.ctxt.StartUnsafePoint(q, c.newprog)
q = obj.Appendp(q, newprog)
q.As = mov
q.Pos = p.Pos
q.From.Type = obj.TYPE_REG
q.From.Reg = REGLINK
q.To.Type = obj.TYPE_MEM
q.To.Offset = int64(-autosize)
q.To.Reg = REGSP
q = obj.Appendp(q, newprog)
q.As = add
q.Pos = p.Pos
q.From.Type = obj.TYPE_CONST
q.From.Offset = int64(-autosize)
q.To.Type = obj.TYPE_REG
q.To.Reg = REGSP
q.Spadj = +autosize
q = c.ctxt.EndUnsafePoint(q, c.newprog, -1)
}
if c.cursym.Func().Text.From.Sym.Wrapper() && c.cursym.Func().Text.Mark&LEAF == 0 {
// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
//
// MOV g_panic(g), R1
// BEQ R1, end
// MOV panic_argp(R1), R2
// ADD $(autosize+FIXED_FRAME), R29, R3
// BNE R2, R3, end
// ADD $FIXED_FRAME, R29, R2
// MOV R2, panic_argp(R1)
// end:
// NOP
//
// The NOP is needed to give the jumps somewhere to land.
// It is a liblink NOP, not an hardware NOP: it encodes to 0 instruction bytes.
//
// We don't generate this for leafs because that means the wrapped
// function was inlined into the wrapper.
q = obj.Appendp(q, newprog)
q.As = mov
q.From.Type = obj.TYPE_MEM
q.From.Reg = REGG
q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
q.To.Type = obj.TYPE_REG
q.To.Reg = REG_R19
q = obj.Appendp(q, newprog)
q.As = ABEQ
q.From.Type = obj.TYPE_REG
q.From.Reg = REG_R19
q.To.Type = obj.TYPE_BRANCH
q.Mark |= BRANCH
p1 = q
q = obj.Appendp(q, newprog)
q.As = mov
q.From.Type = obj.TYPE_MEM
q.From.Reg = REG_R19
q.From.Offset = 0 // Panic.argp
q.To.Type = obj.TYPE_REG
q.To.Reg = REG_R4
q = obj.Appendp(q, newprog)
q.As = add
q.From.Type = obj.TYPE_CONST
q.From.Offset = int64(autosize) + ctxt.Arch.FixedFrameSize
q.Reg = REGSP
q.To.Type = obj.TYPE_REG
q.To.Reg = REG_R5
q = obj.Appendp(q, newprog)
q.As = ABNE
q.From.Type = obj.TYPE_REG
q.From.Reg = REG_R4
q.Reg = REG_R5
q.To.Type = obj.TYPE_BRANCH
q.Mark |= BRANCH
p2 = q
q = obj.Appendp(q, newprog)
q.As = add
q.From.Type = obj.TYPE_CONST
q.From.Offset = ctxt.Arch.FixedFrameSize
q.Reg = REGSP
q.To.Type = obj.TYPE_REG
q.To.Reg = REG_R4
q = obj.Appendp(q, newprog)
q.As = mov
q.From.Type = obj.TYPE_REG
q.From.Reg = REG_R4
q.To.Type = obj.TYPE_MEM
q.To.Reg = REG_R19
q.To.Offset = 0 // Panic.argp
q = obj.Appendp(q, newprog)
q.As = obj.ANOP
p1.To.SetTarget(q)
p2.To.SetTarget(q)
}
case ARET:
if p.From.Type == obj.TYPE_CONST {
ctxt.Diag("using BECOME (%v) is not supported!", p)
break
}
retSym := p.To.Sym
p.To.Name = obj.NAME_NONE // clear fields as we may modify p to other instruction
p.To.Sym = nil
if c.cursym.Func().Text.Mark&LEAF != 0 {
if autosize == 0 {
p.As = AJMP
p.From = obj.Addr{}
if retSym != nil { // retjmp
p.To.Type = obj.TYPE_BRANCH
p.To.Name = obj.NAME_EXTERN
p.To.Sym = retSym
} else {
p.To.Type = obj.TYPE_MEM
p.To.Reg = REGLINK
p.To.Offset = 0
}
p.Mark |= BRANCH
break
}
p.As = add
p.From.Type = obj.TYPE_CONST
p.From.Offset = int64(autosize)
p.To.Type = obj.TYPE_REG
p.To.Reg = REGSP
p.Spadj = -autosize
q = c.newprog()
q.As = AJMP
q.Pos = p.Pos
if retSym != nil { // retjmp
q.To.Type = obj.TYPE_BRANCH
q.To.Name = obj.NAME_EXTERN
q.To.Sym = retSym
} else {
q.To.Type = obj.TYPE_MEM
q.To.Offset = 0
q.To.Reg = REGLINK
}
q.Mark |= BRANCH
q.Spadj = +autosize
q.Link = p.Link
p.Link = q
break
}
p.As = mov
p.From.Type = obj.TYPE_MEM
p.From.Offset = 0
p.From.Reg = REGSP
p.To.Type = obj.TYPE_REG
p.To.Reg = REGLINK
if autosize != 0 {
q = c.newprog()
q.As = add
q.Pos = p.Pos
q.From.Type = obj.TYPE_CONST
q.From.Offset = int64(autosize)
q.To.Type = obj.TYPE_REG
q.To.Reg = REGSP
q.Spadj = -autosize
q.Link = p.Link
p.Link = q
}
q1 = c.newprog()
q1.As = AJMP
q1.Pos = p.Pos
if retSym != nil { // retjmp
q1.To.Type = obj.TYPE_BRANCH
q1.To.Name = obj.NAME_EXTERN
q1.To.Sym = retSym
} else {
q1.To.Type = obj.TYPE_MEM
q1.To.Offset = 0
q1.To.Reg = REGLINK
}
q1.Mark |= BRANCH
q1.Spadj = +autosize
q1.Link = q.Link
q.Link = q1
case AADD,
AADDU,
AADDV,
AADDVU:
if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
p.Spadj = int32(-p.From.Offset)
}
case obj.AGETCALLERPC:
if cursym.Leaf() {
// MOV LR, Rd
p.As = mov
p.From.Type = obj.TYPE_REG
p.From.Reg = REGLINK
} else {
// MOV (RSP), Rd
p.As = mov
p.From.Type = obj.TYPE_MEM
p.From.Reg = REGSP
}
}
}
}
func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
var mov, add obj.As
add = AADDV
mov = AMOVV
if c.ctxt.Flag_maymorestack != "" {
// Save LR and REGCTXT.
frameSize := 2 * c.ctxt.Arch.PtrSize
p = c.ctxt.StartUnsafePoint(p, c.newprog)
// MOV REGLINK, -8/-16(SP)
p = obj.Appendp(p, c.newprog)
p.As = mov
p.From.Type = obj.TYPE_REG
p.From.Reg = REGLINK
p.To.Type = obj.TYPE_MEM
p.To.Offset = int64(-frameSize)
p.To.Reg = REGSP
// MOV REGCTXT, -4/-8(SP)
p = obj.Appendp(p, c.newprog)
p.As = mov
p.From.Type = obj.TYPE_REG
p.From.Reg = REGCTXT
p.To.Type = obj.TYPE_MEM
p.To.Offset = -int64(c.ctxt.Arch.PtrSize)
p.To.Reg = REGSP
// ADD $-8/$-16, SP
p = obj.Appendp(p, c.newprog)
p.As = add
p.From.Type = obj.TYPE_CONST
p.From.Offset = int64(-frameSize)
p.To.Type = obj.TYPE_REG
p.To.Reg = REGSP
p.Spadj = int32(frameSize)
// JAL maymorestack
p = obj.Appendp(p, c.newprog)
p.As = AJAL
p.To.Type = obj.TYPE_BRANCH
// See ../x86/obj6.go
p.To.Sym = c.ctxt.LookupABI(c.ctxt.Flag_maymorestack, c.cursym.ABI())
p.Mark |= BRANCH
// Restore LR and REGCTXT.
// MOV 0(SP), REGLINK
p = obj.Appendp(p, c.newprog)
p.As = mov
p.From.Type = obj.TYPE_MEM
p.From.Offset = 0
p.From.Reg = REGSP
p.To.Type = obj.TYPE_REG
p.To.Reg = REGLINK
// MOV 4/8(SP), REGCTXT
p = obj.Appendp(p, c.newprog)
p.As = mov
p.From.Type = obj.TYPE_MEM
p.From.Offset = int64(c.ctxt.Arch.PtrSize)
p.From.Reg = REGSP
p.To.Type = obj.TYPE_REG
p.To.Reg = REGCTXT
// ADD $8/$16, SP
p = obj.Appendp(p, c.newprog)
p.As = add
p.From.Type = obj.TYPE_CONST
p.From.Offset = int64(frameSize)
p.To.Type = obj.TYPE_REG
p.To.Reg = REGSP
p.Spadj = int32(-frameSize)
p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
}
// Jump back to here after morestack returns.
startPred := p
// MOV g_stackguard(g), R19
p = obj.Appendp(p, c.newprog)
p.As = mov
p.From.Type = obj.TYPE_MEM
p.From.Reg = REGG
p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
if c.cursym.CFunc() {
p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
}
p.To.Type = obj.TYPE_REG
p.To.Reg = REG_R19
// 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 = c.ctxt.StartUnsafePoint(p, c.newprog)
var q *obj.Prog
if framesize <= objabi.StackSmall {
// small stack: SP < stackguard
// AGTU SP, stackguard, R19
p = obj.Appendp(p, c.newprog)
p.As = ASGTU
p.From.Type = obj.TYPE_REG
p.From.Reg = REGSP
p.Reg = REG_R19
p.To.Type = obj.TYPE_REG
p.To.Reg = REG_R19
} else {
// large stack: SP-framesize < stackguard-StackSmall
offset := int64(framesize) - objabi.StackSmall
if framesize > objabi.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.
//
// SGTU $(framesize-StackSmall), SP, R4
// BNE R4, label-of-call-to-morestack
p = obj.Appendp(p, c.newprog)
p.As = ASGTU
p.From.Type = obj.TYPE_CONST
p.From.Offset = offset
p.Reg = REGSP
p.To.Type = obj.TYPE_REG
p.To.Reg = REG_R4
p = obj.Appendp(p, c.newprog)
q = p
p.As = ABNE
p.From.Type = obj.TYPE_REG
p.From.Reg = REG_R4
p.To.Type = obj.TYPE_BRANCH
p.Mark |= BRANCH
}
p = obj.Appendp(p, c.newprog)
p.As = add
p.From.Type = obj.TYPE_CONST
p.From.Offset = -offset
p.Reg = REGSP
p.To.Type = obj.TYPE_REG
p.To.Reg = REG_R4
p = obj.Appendp(p, c.newprog)
p.As = ASGTU
p.From.Type = obj.TYPE_REG
p.From.Reg = REG_R4
p.Reg = REG_R19
p.To.Type = obj.TYPE_REG
p.To.Reg = REG_R19
}
// q1: BNE R19, done
p = obj.Appendp(p, c.newprog)
q1 := p
p.As = ABNE
p.From.Type = obj.TYPE_REG
p.From.Reg = REG_R19
p.To.Type = obj.TYPE_BRANCH
p.Mark |= BRANCH
// MOV LINK, R5
p = obj.Appendp(p, c.newprog)
p.As = mov
p.From.Type = obj.TYPE_REG
p.From.Reg = REGLINK
p.To.Type = obj.TYPE_REG
p.To.Reg = REG_R5
if q != nil {
q.To.SetTarget(p)
p.Mark |= LABEL
}
p = c.ctxt.EmitEntryStackMap(c.cursym, p, c.newprog)
// JAL runtime.morestack(SB)
p = obj.Appendp(p, c.newprog)
p.As = AJAL
p.To.Type = obj.TYPE_BRANCH
if c.cursym.CFunc() {
p.To.Sym = c.ctxt.Lookup("runtime.morestackc")
} else if !c.cursym.Func().Text.From.Sym.NeedCtxt() {
p.To.Sym = c.ctxt.Lookup("runtime.morestack_noctxt")
} else {
p.To.Sym = c.ctxt.Lookup("runtime.morestack")
}
p.Mark |= BRANCH
p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
// JMP start
p = obj.Appendp(p, c.newprog)
p.As = AJMP
p.To.Type = obj.TYPE_BRANCH
p.To.SetTarget(startPred.Link)
startPred.Link.Mark |= LABEL
p.Mark |= BRANCH
// placeholder for q1's jump target
p = obj.Appendp(p, c.newprog)
p.As = obj.ANOP // zero-width place holder
q1.To.SetTarget(p)
return p
}
func (c *ctxt0) addnop(p *obj.Prog) {
q := c.newprog()
q.As = ANOOP
q.Pos = p.Pos
q.Link = p.Link
p.Link = q
}
var Linkloong64 = obj.LinkArch{
Arch: sys.ArchLoong64,
Init: buildop,
Preprocess: preprocess,
Assemble: span0,
Progedit: progedit,
DWARFRegisters: LOONG64DWARFRegisters,
}

View File

@ -507,15 +507,16 @@ var regSpace []regSet
const ( const (
// Because of masking operations in the encodings, each register // Because of masking operations in the encodings, each register
// space should start at 0 modulo some power of 2. // space should start at 0 modulo some power of 2.
RBase386 = 1 * 1024 RBase386 = 1 * 1024
RBaseAMD64 = 2 * 1024 RBaseAMD64 = 2 * 1024
RBaseARM = 3 * 1024 RBaseARM = 3 * 1024
RBasePPC64 = 4 * 1024 // range [4k, 8k) RBasePPC64 = 4 * 1024 // range [4k, 8k)
RBaseARM64 = 8 * 1024 // range [8k, 13k) RBaseARM64 = 8 * 1024 // range [8k, 13k)
RBaseMIPS = 13 * 1024 // range [13k, 14k) RBaseMIPS = 13 * 1024 // range [13k, 14k)
RBaseS390X = 14 * 1024 // range [14k, 15k) RBaseS390X = 14 * 1024 // range [14k, 15k)
RBaseRISCV = 15 * 1024 // range [15k, 16k) RBaseRISCV = 15 * 1024 // range [15k, 16k)
RBaseWasm = 16 * 1024 RBaseWasm = 16 * 1024
RBaseLOONG64 = 17 * 1024
) )
// RegisterRegister binds a pretty-printer (Rconv) for register // RegisterRegister binds a pretty-printer (Rconv) for register