blob: dc05e18c7d85d260200a71f435b1b5bbb3e43c7a [file] [log] [blame]
// 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"
"log"
"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
}
}
if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 {
f := c.cursym.Func()
if f.FuncFlag&objabi.FuncFlag_SPWRITE == 0 {
c.cursym.Func().FuncFlag |= objabi.FuncFlag_SPWRITE
if ctxt.Debugvlog || !ctxt.IsAsm {
ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p)
if !ctxt.IsAsm {
ctxt.Diag("invalid auto-SPWRITE in non-assembly")
ctxt.DiagFlush()
log.Fatalf("bad SPWRITE")
}
}
}
}
}
}
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,
}