feat(asm): emit the loong64 stack-split guard for big frames
This commit is contained in:
+11
-4
@@ -182,7 +182,7 @@ func arm64Prologue(fi arm64FrameInfo) []byte {
|
|||||||
|
|
||||||
// arm64SubImmWords emits SUB $imm, SP, Rd: the immediate form when the value
|
// arm64SubImmWords emits SUB $imm, SP, Rd: the immediate form when the value
|
||||||
// fits the imm12 field, otherwise the toolchain materialises it into REGTMP
|
// fits the imm12 field, otherwise the toolchain materialises it into REGTMP
|
||||||
// (R27) and subtracts the register.
|
// (R27) and subtracts the register in the extended-register form.
|
||||||
func arm64SubImmWords(imm uint32, rd uint32) []uint32 {
|
func arm64SubImmWords(imm uint32, rd uint32) []uint32 {
|
||||||
if imm <= 0xFFF {
|
if imm <= 0xFFF {
|
||||||
return []uint32{a64AddSub(1, 1, 0, 0, imm, 31, rd)}
|
return []uint32{a64AddSub(1, 1, 0, 0, imm, 31, rd)}
|
||||||
@@ -191,7 +191,7 @@ func arm64SubImmWords(imm uint32, rd uint32) []uint32 {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
mov = nil
|
mov = nil
|
||||||
}
|
}
|
||||||
return append(wordsOf(mov), arm64DPSRWords(arm64OpSub, 27, 31, rd))
|
return append(wordsOf(mov), arm64DPExtWords(arm64OpSub, 27, 31, rd))
|
||||||
}
|
}
|
||||||
|
|
||||||
// arm64AddImmWords emits ADD $imm, SP, Rd with the same REGTMP fallback.
|
// arm64AddImmWords emits ADD $imm, SP, Rd with the same REGTMP fallback.
|
||||||
@@ -203,7 +203,7 @@ func arm64AddImmWords(imm uint32, rd uint32) []uint32 {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
mov = nil
|
mov = nil
|
||||||
}
|
}
|
||||||
return append(wordsOf(mov), arm64DPSRWords(arm64OpAdd, 27, 31, rd))
|
return append(wordsOf(mov), arm64DPExtWords(arm64OpAdd, 27, 31, rd))
|
||||||
}
|
}
|
||||||
|
|
||||||
// arm64Return returns the bytes for a RET: the epilogue (restore FP/LR and
|
// arm64Return returns the bytes for a RET: the epilogue (restore FP/LR and
|
||||||
@@ -326,6 +326,13 @@ func arm64DPSRWords(base uint32, rm, rn, rd uint32) uint32 {
|
|||||||
return base | rm<<16 | rn<<5 | rd
|
return base | rm<<16 | rn<<5 | rd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// arm64DPExtWords builds one data-processing (extended register) word, the
|
||||||
|
// form the toolchain picks when a large immediate was materialised into
|
||||||
|
// REGTMP before the operation: base | 1<<21 | Rm<<16 | UXTX<<13 | Rn<<5 | Rd.
|
||||||
|
func arm64DPExtWords(base, rm, rn, rd uint32) uint32 {
|
||||||
|
return base | 1<<21 | rm<<16 | 3<<13 | rn<<5 | rd
|
||||||
|
}
|
||||||
|
|
||||||
// wordsOf converts little-endian instruction bytes back to words.
|
// wordsOf converts little-endian instruction bytes back to words.
|
||||||
func wordsOf(b []byte) []uint32 {
|
func wordsOf(b []byte) []uint32 {
|
||||||
ws := make([]uint32, 0, len(b)/4)
|
ws := make([]uint32, 0, len(b)/4)
|
||||||
@@ -359,7 +366,7 @@ func arm64GuardBytes(fi arm64FrameInfo, blockStart int) []byte {
|
|||||||
}
|
}
|
||||||
ws = append(ws, wordsOf(mov)...)
|
ws = append(ws, wordsOf(mov)...)
|
||||||
ml := len(mov) / 4
|
ml := len(mov) / 4
|
||||||
ws = append(ws, arm64DPSRWords(arm64OpSubs, 27, 31, 17)) // SUBS R27, RSP, R17
|
ws = append(ws, arm64DPExtWords(arm64OpSubs, 27, 31, 17)) // SUBS R17, RSP, R27
|
||||||
ws = append(ws, br(8+ml, a64CondLO))
|
ws = append(ws, br(8+ml, a64CondLO))
|
||||||
ws = append(ws, arm64DPSRWords(arm64OpSubs, 16, 17, 31)) // CMP R16, R17
|
ws = append(ws, arm64DPSRWords(arm64OpSubs, 16, 17, 31)) // CMP R16, R17
|
||||||
ws = append(ws, br(8+ml+8, a64CondLS))
|
ws = append(ws, br(8+ml+8, a64CondLS))
|
||||||
|
|||||||
+5
-1
@@ -16,6 +16,7 @@ const (
|
|||||||
// LoongArch relocation types (the ELF psABI).
|
// LoongArch relocation types (the ELF psABI).
|
||||||
rLarchPCALAHI20 = 71 // R_LARCH_PCALA_HI20 (pcalau12i)
|
rLarchPCALAHI20 = 71 // R_LARCH_PCALA_HI20 (pcalau12i)
|
||||||
rLarchPCALALO12 = 72 // R_LARCH_PCALA_LO12 (addi.d/ld/st)
|
rLarchPCALALO12 = 72 // R_LARCH_PCALA_LO12 (addi.d/ld/st)
|
||||||
|
rLarchB26 = 66 // R_LARCH_B26 (b/bl, matches the Go linker's mapping)
|
||||||
)
|
)
|
||||||
|
|
||||||
// ELFLOONG64Object returns the image as an ELF64 relocatable object file for
|
// ELFLOONG64Object returns the image as an ELF64 relocatable object file for
|
||||||
@@ -95,8 +96,11 @@ func (img *Image) ELFLOONG64Object() ([]byte, error) {
|
|||||||
return nil, fmt.Errorf("relocation references unknown symbol %q", r.Name)
|
return nil, fmt.Errorf("relocation references unknown symbol %q", r.Name)
|
||||||
}
|
}
|
||||||
typ := uint32(rLarchPCALAHI20)
|
typ := uint32(rLarchPCALAHI20)
|
||||||
if r.Kind == RelLoong64AddrLo {
|
switch r.Kind {
|
||||||
|
case RelLoong64AddrLo:
|
||||||
typ = rLarchPCALALO12
|
typ = rLarchPCALALO12
|
||||||
|
case RelLoong64Branch:
|
||||||
|
typ = rLarchB26
|
||||||
}
|
}
|
||||||
relas = append(relas, elfRela{
|
relas = append(relas, elfRela{
|
||||||
off: uint64(fn.Offset + r.Off),
|
off: uint64(fn.Offset + r.Off),
|
||||||
|
|||||||
@@ -198,3 +198,45 @@ TEXT ·nop(SB), NOSPLIT, $0
|
|||||||
t.Error("function symbol nop not found")
|
t.Error("function symbol nop not found")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestELFLOONG64BranchRelocation checks that the morestack call and an
|
||||||
|
// internal CALL both carry R_LARCH_B26 in the emitted object, matching the
|
||||||
|
// Go linker's mapping of its call relocation.
|
||||||
|
func TestELFLOONG64BranchRelocation(t *testing.T) {
|
||||||
|
f, errs := parser.Parse("k_loong64.s", "TEXT \u00b7callbig(SB), $8192-0\n\tCALL \u00b7other(SB)\n\tRET\nTEXT \u00b7other(SB), NOSPLIT, $0\n\tRET\n")
|
||||||
|
if len(errs) > 0 {
|
||||||
|
t.Fatalf("parse: %v", errs)
|
||||||
|
}
|
||||||
|
img, err := AssembleFileLOONG64(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("AssembleFileLOONG64: %v", err)
|
||||||
|
}
|
||||||
|
obj, err := img.ELFLOONG64Object()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ELFLOONG64Object: %v", err)
|
||||||
|
}
|
||||||
|
ef, err := elf.NewFile(bytes.NewReader(obj))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("parse emitted object: %v", err)
|
||||||
|
}
|
||||||
|
defer ef.Close()
|
||||||
|
relaSec := ef.Section(".rela.text")
|
||||||
|
if relaSec == nil {
|
||||||
|
t.Fatal("missing .rela.text")
|
||||||
|
}
|
||||||
|
raw, err := relaSec.Data()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// The guard's morestack call plus the body's CALL to other.
|
||||||
|
if len(raw)%24 != 0 || len(raw)/24 != 2 {
|
||||||
|
t.Fatalf(".rela.text has %d bytes, want two 24-byte entries", len(raw))
|
||||||
|
}
|
||||||
|
le := binary.LittleEndian
|
||||||
|
for i := range 2 {
|
||||||
|
info := le.Uint64(raw[i*24+8:])
|
||||||
|
if elf.R_LARCH(info&0xffffffff) != elf.R_LARCH_B26 {
|
||||||
|
t.Errorf("relocation %d type = %v, want R_LARCH_B26", i, elf.R_LARCH(info&0xffffffff))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+11
-1
@@ -156,6 +156,16 @@ const goobjBuiltinMorestackNoctxt = 246
|
|||||||
// stack-guard call.
|
// stack-guard call.
|
||||||
var goobjBuiltinMorestack = "runtime\u00b7morestack_noctxt"
|
var goobjBuiltinMorestack = "runtime\u00b7morestack_noctxt"
|
||||||
|
|
||||||
|
// isCallReloc reports whether k is one of the per-arch call relocations a
|
||||||
|
// direct branch to a TEXT symbol carries.
|
||||||
|
func isCallReloc(k RelocKind) bool {
|
||||||
|
switch k {
|
||||||
|
case RelCall, RelRISCVJal, RelArm64Branch, RelLoong64Branch:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
const goobjMagic = "\x00go120ld"
|
const goobjMagic = "\x00go120ld"
|
||||||
|
|
||||||
// goSym is one symbol definition under construction.
|
// goSym is one symbol definition under construction.
|
||||||
@@ -436,7 +446,7 @@ func (img *Image) emitGOObject(pkgPath, srcPath string, pre []byte, minLC int, r
|
|||||||
// A call to a TEXT function of the same file references the
|
// A call to a TEXT function of the same file references the
|
||||||
// non-package definition table.
|
// non-package definition table.
|
||||||
ni, isText := textNpIdx[r.Name]
|
ni, isText := textNpIdx[r.Name]
|
||||||
if !isText || r.Kind != RelCall {
|
if !isText || !isCallReloc(r.Kind) {
|
||||||
return nil, fmt.Errorf("GOOBJ emission: reference to unknown symbol %q", r.Name)
|
return nil, fmt.Errorf("GOOBJ emission: reference to unknown symbol %q", r.Name)
|
||||||
}
|
}
|
||||||
pkg = pkgIdxNone
|
pkg = pkgIdxNone
|
||||||
|
|||||||
+62
-5
@@ -8,6 +8,7 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"sourcedock.dev/petrbalvin/gasm-devkit/ast"
|
||||||
"sourcedock.dev/petrbalvin/gasm-devkit/parser"
|
"sourcedock.dev/petrbalvin/gasm-devkit/parser"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -116,7 +117,7 @@ func TestStackGuardBytesARM64(t *testing.T) {
|
|||||||
{"leafmed", "TEXT \u00b7leafmed(SB), $256-0\n\tRET\n",
|
{"leafmed", "TEXT \u00b7leafmed(SB), $256-0\n\tRET\n",
|
||||||
"900b40f9f14302d13f0210eb09010054f44304d19dfa3fa99f020091fd2300d1fd230491ff430491c0035fd6e3031eaa00000000f3ffff17"},
|
"900b40f9f14302d13f0210eb09010054f44304d19dfa3fa99f020091fd2300d1fd230491ff430491c0035fd6e3031eaa00000000f3ffff17"},
|
||||||
{"leafbig", "TEXT \u00b7leafbig(SB), $8192-0\n\tRET\n",
|
{"leafbig", "TEXT \u00b7leafbig(SB), $8192-0\n\tRET\n",
|
||||||
"900b40f91bf283d2f1031beba30100543f0210eb690100541b0284d2f4031bcb9dfa3fa99f020091fd2300d11b0184d2fd031b8b1b0284d2ff031b8bc0035fd6e3031eaa00000000eeffff17"},
|
"900b40f91bf283d2f1633beba30100543f0210eb690100541b0284d2f4633bcb9dfa3fa99f020091fd2300d11b0184d2fd633b8b1b0284d2ff633b8bc0035fd6e3031eaa00000000eeffff17"},
|
||||||
{"callsmall", "TEXT \u00b7callsmall(SB), $16-0\n\tCALL \u00b7other(SB)\n\tRET\nTEXT \u00b7other(SB), NOSPLIT, $0\n\tRET\n",
|
{"callsmall", "TEXT \u00b7callsmall(SB), $16-0\n\tCALL \u00b7other(SB)\n\tRET\nTEXT \u00b7other(SB), NOSPLIT, $0\n\tRET\n",
|
||||||
"900b40f9ff6330eb09010054fe0f1ef8fd831ff8fd2300d100000000fd835ff8fe0742f8c0035fd6e3031eaa00000000f4ffff17"},
|
"900b40f9ff6330eb09010054fe0f1ef8fd831ff8fd2300d100000000fd835ff8fe0742f8c0035fd6e3031eaa00000000f4ffff17"},
|
||||||
{"nosplit", "TEXT \u00b7nosplit(SB), NOSPLIT, $16-0\n\tRET\n",
|
{"nosplit", "TEXT \u00b7nosplit(SB), NOSPLIT, $16-0\n\tRET\n",
|
||||||
@@ -187,9 +188,10 @@ func TestStackGuardBytesRISCV64(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The loong64 stack-split guard, pinned from `go tool asm` (Go 1.27,
|
// The loong64 stack-split guard, pinned from `go tool asm` (Go 1.27,
|
||||||
// loong64) for the small and medium frame classes plus auto-NOSPLIT. The
|
// loong64): every guard class (including the medium class with the
|
||||||
// big class (>StackBig) and the huge-frame body prologue remain toolchain
|
// materialised constant and the big class with the ORI-less constants), the
|
||||||
// divergences tracked separately.
|
// auto-NOSPLIT leaf behaviour, the large-frame R30 prologue/epilogue forms
|
||||||
|
// and the morestack block. Relocation fields are masked.
|
||||||
func TestStackGuardBytesLOONG64(t *testing.T) {
|
func TestStackGuardBytesLOONG64(t *testing.T) {
|
||||||
for _, tt := range []struct {
|
for _, tt := range []struct {
|
||||||
name string
|
name string
|
||||||
@@ -199,9 +201,37 @@ func TestStackGuardBytesLOONG64(t *testing.T) {
|
|||||||
{"leafsmall", "TEXT \u00b7leafsmall(SB), $16-0\n\tRET\n",
|
{"leafsmall", "TEXT \u00b7leafsmall(SB), $16-0\n\tRET\n",
|
||||||
"61a0ff2963a0ff026100c0296360c0022000004c"},
|
"61a0ff2963a0ff026100c0296360c0022000004c"},
|
||||||
{"leafmed", "TEXT \u00b7leafmed(SB), $256-0\n\tRET\n",
|
{"leafmed", "TEXT \u00b7leafmed(SB), $256-0\n\tRET\n",
|
||||||
"d442c02878e0fd0294e21200801a004061e0fb2963e0fb026100c0296320c4022000004c3f00100000000000ffd7ff53"},
|
"d442c02878e0fd0294e21200801a004061e0fb2963e0fb026100c0296320c4022000004c3f00150000000000ffd7ff53"},
|
||||||
{"nosplit", "TEXT \u00b7nosplit(SB), NOSPLIT, $16-0\n\tRET\n",
|
{"nosplit", "TEXT \u00b7nosplit(SB), NOSPLIT, $16-0\n\tRET\n",
|
||||||
"61a0ff2963a0ff026100c0296360c0022000004c"},
|
"61a0ff2963a0ff026100c0296360c0022000004c"},
|
||||||
|
// The LR store leaves the 12-bit store-offset range while the SP
|
||||||
|
// adjust immediate still fits, and the epilogue adjusts through a
|
||||||
|
// single ORI.
|
||||||
|
{"fit2048", "TEXT \u00b7fit2048(SB), $2040-0\n\tRET\n",
|
||||||
|
"d442c0287800e20294e21200802600401e000014de8f1000c103e0296300e0026100c0291e00a00363f810002000004c3f00150000000000ffcbff53"},
|
||||||
|
// Medium class at the materialisation boundary (off = 2048 still
|
||||||
|
// immediate, 2049+ goes through R30).
|
||||||
|
{"med2048off", "TEXT \u00b7med2048off(SB), $2168-0\n\tRET\n",
|
||||||
|
"d442c0287800e00294e21200802e0040feffff15de8f1000c103de29feffff15de039e0363f810006100c0291e00a20363f810002000004c3f00150000000000ffc3ff53"},
|
||||||
|
{"medmat", "TEXT \u00b7medmat(SB), $2176-0\n\tRET\n",
|
||||||
|
"d442c028feffff15dee39f0378f8100094e21200802e0040feffff15de8f1000c1e3dd29feffff15dee39d0363f810006100c0291e20a20363f810002000004c3f00150000000000ffbbff53"},
|
||||||
|
// Big class with the rounding-split store and the floor-split adjust.
|
||||||
|
{"leafbig", "TEXT \u00b7leafbig(SB), $8192-0\n\tRET\n",
|
||||||
|
"d442c0283e000014de23be0378f8120000470044deffff15dee3810378f8100094e2120080320040deffff15de8f1000c1e3ff29beffff15dee3bf0363f810006100c0295e000014de23800363f810002000004c3f00150000000000ffa7ff53"},
|
||||||
|
// Zero low 12 bits drop the ORI from the store, the adjust and the
|
||||||
|
// epilogue materialisation.
|
||||||
|
{"bigzero", "TEXT \u00b7bigzero(SB), $4088-0\n\tRET\n",
|
||||||
|
"d442c028feffff15de03820378f8100094e21200802a0040feffff15de8f1000c103c029feffff1563f810006100c0293e00001463f810002000004c3f00150000000000ffbfff53"},
|
||||||
|
// Big class whose first constant has a zero high part: a single ORI.
|
||||||
|
{"big3976", "TEXT \u00b7big3976(SB), $4096-0\n\tRET\n",
|
||||||
|
"d442c0281e20be0378f8120000470044feffff15dee3810378f8100094e2120080320040feffff15de8f1000c1e3ff29deffff15dee3bf0363f810006100c0293e000014de23800363f810002000004c3f00150000000000ffabff53"},
|
||||||
|
// Big class at a multiple of 4096: both guard constants lose their
|
||||||
|
// ORI word.
|
||||||
|
{"giantlo0", "TEXT \u00b7giantlo0(SB), $4216-0\n\tRET\n",
|
||||||
|
"d442c0283e00001478f8120000430044feffff1578f8100094e2120080320040feffff15de8f1000c103fe29deffff15de03be0363f810006100c0293e000014de03820363f810002000004c3f00150000000000ffafff53"},
|
||||||
|
// Non-leaf big frame: the body call plus the LR restore epilogue.
|
||||||
|
{"callbig", "TEXT \u00b7callbig(SB), $8192-0\n\tCALL \u00b7other(SB)\n\tRET\nTEXT \u00b7other(SB), NOSPLIT, $0\n\tRET\n",
|
||||||
|
"d442c0283e000014de23be0378f81200004f0044deffff15dee3810378f8100094e21200803a0040deffff15de8f1000c1e3ff29beffff15dee3bf0363f810006100c029000000006100c0285e000014de23800363f810002000004c3f00150000000000ff9fff53"},
|
||||||
} {
|
} {
|
||||||
f, errs := parser.Parse("g_loong64.s", tt.src)
|
f, errs := parser.Parse("g_loong64.s", tt.src)
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
@@ -224,3 +254,30 @@ func TestStackGuardBytesLOONG64(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestStackGuardGOObjInternalCall checks that GOOBJ emission succeeds when a
|
||||||
|
// guarded function calls a TEXT symbol of the same file, for every arch's
|
||||||
|
// call relocation kind.
|
||||||
|
func TestStackGuardGOObjInternalCall(t *testing.T) {
|
||||||
|
for _, tt := range []struct {
|
||||||
|
src string
|
||||||
|
assemble func(*ast.File) (*Image, error)
|
||||||
|
}{
|
||||||
|
{"g_amd64.s", AssembleFile},
|
||||||
|
{"g_arm64.s", AssembleFileARM64},
|
||||||
|
{"g_riscv64.s", AssembleFileRISCV},
|
||||||
|
{"g_loong64.s", AssembleFileLOONG64},
|
||||||
|
} {
|
||||||
|
f, errs := parser.Parse(tt.src, "TEXT \u00b7callsmall(SB), $16-0\n\tCALL \u00b7other(SB)\n\tRET\nTEXT \u00b7other(SB), NOSPLIT, $0\n\tRET\n")
|
||||||
|
if len(errs) > 0 {
|
||||||
|
t.Fatalf("%s: parse: %v", tt.src, errs)
|
||||||
|
}
|
||||||
|
img, err := tt.assemble(f)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%s: assemble: %v", tt.src, err)
|
||||||
|
}
|
||||||
|
if _, err := img.GOObject("testpkg", tt.src); err != nil {
|
||||||
|
t.Errorf("%s: GOObject: %v", tt.src, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+22
-20
@@ -18,12 +18,10 @@ import (
|
|||||||
// then encoding with resolved branch targets).
|
// then encoding with resolved branch targets).
|
||||||
//
|
//
|
||||||
// The emitted bytes match the Go toolchain's loong64 assembler, which is the
|
// The emitted bytes match the Go toolchain's loong64 assembler, which is the
|
||||||
// ground-truth oracle: prologue/epilogue, FP/SP frame mapping, branch
|
// ground-truth oracle: prologue/epilogue (including the large-frame R30
|
||||||
// encodings and the MOV immediate expansions all follow cmd/internal/obj/
|
// materialisations), FP/SP frame mapping, the stack-split guard classes, and
|
||||||
// loong64's asmout cases. One deliberate difference: the stack-growth guard
|
// branch encodings all follow cmd/internal/obj/loong64. The morestack block
|
||||||
// (the morestack check in the prologue and the call back into the runtime in
|
// at the end of split functions carries the runtime.morestack_noctxt call.
|
||||||
// the epilogue) is not emitted, so the bytes match only for NOSPLIT functions
|
|
||||||
// or zero-frame leaves, where the toolchain emits no guard either.
|
|
||||||
func assembleLOONG64(t *ast.Text) ([]byte, map[string]int, []Reloc, []LineEntry, []SpadjStep, error) {
|
func assembleLOONG64(t *ast.Text) ([]byte, map[string]int, []Reloc, []LineEntry, []SpadjStep, error) {
|
||||||
fi := loong64ComputeFrame(t)
|
fi := loong64ComputeFrame(t)
|
||||||
prologue := loong64Prologue(fi)
|
prologue := loong64Prologue(fi)
|
||||||
@@ -39,11 +37,12 @@ func assembleLOONG64(t *ast.Text) ([]byte, map[string]int, []Reloc, []LineEntry,
|
|||||||
var relocs []Reloc
|
var relocs []Reloc
|
||||||
var spadj []SpadjStep
|
var spadj []SpadjStep
|
||||||
|
|
||||||
// The prologue (3 instructions when a frame is present) raises the SP
|
// The prologue raises the SP delta by autosize; the boundary is reported
|
||||||
// delta by autosize; the boundary is reported at the third instruction's
|
// after the SP adjust instruction, exactly as the toolchain's pctospadj
|
||||||
// pc, exactly as the toolchain's pctospadj does.
|
// does. The prologue (3 instructions when a frame is present) may
|
||||||
|
// materialise its store or adjust through R30, which widens it.
|
||||||
if fi.autosize != 0 {
|
if fi.autosize != 0 {
|
||||||
spadj = append(spadj, SpadjStep{PC: guardLen + 8, Value: fi.autosize})
|
spadj = append(spadj, SpadjStep{PC: guardLen + (loong64StoreWords(fi.autosize)+loong64AdjustWords(-int64(fi.autosize)))*4, Value: fi.autosize})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass 1: label offsets from the instruction sizes.
|
// Pass 1: label offsets from the instruction sizes.
|
||||||
@@ -99,14 +98,9 @@ func assembleLOONG64(t *ast.Text) ([]byte, map[string]int, []Reloc, []LineEntry,
|
|||||||
preCount = len(relocs)
|
preCount = len(relocs)
|
||||||
lines = append(lines, LineEntry{Offset: pc, Line: in.Pos().Line})
|
lines = append(lines, LineEntry{Offset: pc, Line: in.Pos().Line})
|
||||||
// The RET's epilogue closes the frame: the SP delta returns to zero
|
// The RET's epilogue closes the frame: the SP delta returns to zero
|
||||||
// after the addi.d (one instruction for a leaf, two for a non-leaf
|
// after the frame-deallocating ADDV.
|
||||||
// with the LR restore).
|
|
||||||
if strings.ToUpper(in.Mnemonic.Text) == "RET" && fi.autosize != 0 {
|
if strings.ToUpper(in.Mnemonic.Text) == "RET" && fi.autosize != 0 {
|
||||||
epi := 4
|
spadj = append(spadj, SpadjStep{PC: pc + loong64EpilogueWords(fi)*4, Value: 0})
|
||||||
if !fi.leaf {
|
|
||||||
epi = 8
|
|
||||||
}
|
|
||||||
spadj = append(spadj, SpadjStep{PC: pc + epi, Value: 0})
|
|
||||||
}
|
}
|
||||||
out = append(out, code...)
|
out = append(out, code...)
|
||||||
pc += len(code)
|
pc += len(code)
|
||||||
@@ -255,9 +249,9 @@ func encodeLOONG64Instr(instr *ast.Instr, pc int, offsets map[string]int, fi loo
|
|||||||
}
|
}
|
||||||
return l64wordLE(uint32(immFromOperand(ops[0]))), nil
|
return l64wordLE(uint32(immFromOperand(ops[0]))), nil
|
||||||
case "JMP", "B":
|
case "JMP", "B":
|
||||||
return encodeLOONG64Branch(instr, mnem, pc, offsets, false, resolve)
|
return encodeLOONG64Branch(instr, mnem, pc, offsets, false, resolve, relocs)
|
||||||
case "JAL", "CALL", "BL":
|
case "JAL", "CALL", "BL":
|
||||||
return encodeLOONG64Branch(instr, mnem, pc, offsets, true, resolve)
|
return encodeLOONG64Branch(instr, mnem, pc, offsets, true, resolve, relocs)
|
||||||
case "MOV", "MOVB", "MOVH", "MOVW", "MOVV", "MOVBU", "MOVHU", "MOVWU", "MOVF", "MOVD":
|
case "MOV", "MOVB", "MOVH", "MOVW", "MOVV", "MOVBU", "MOVHU", "MOVWU", "MOVF", "MOVD":
|
||||||
return encodeLOONG64Mov(instr, mnem, fi, relocs)
|
return encodeLOONG64Mov(instr, mnem, fi, relocs)
|
||||||
}
|
}
|
||||||
@@ -516,7 +510,7 @@ func encodeLOONG64Instr(instr *ast.Instr, pc int, offsets map[string]int, fi loo
|
|||||||
//
|
//
|
||||||
// JMP/B label → b label JMP/B (rj) → jirl r0, rj, 0
|
// JMP/B label → b label JMP/B (rj) → jirl r0, rj, 0
|
||||||
// JAL/CALL/BL label → bl label JAL/CALL/BL (rj) → jirl r1, rj, 0
|
// JAL/CALL/BL label → bl label JAL/CALL/BL (rj) → jirl r1, rj, 0
|
||||||
func encodeLOONG64Branch(instr *ast.Instr, mnem string, pc int, offsets map[string]int, link bool, resolve func(string) string) ([]byte, error) {
|
func encodeLOONG64Branch(instr *ast.Instr, mnem string, pc int, offsets map[string]int, link bool, resolve func(string) string, relocs *[]Reloc) ([]byte, error) {
|
||||||
if len(instr.Operands) != 1 {
|
if len(instr.Operands) != 1 {
|
||||||
return nil, fmt.Errorf("%s expects 1 operand, got %d", mnem, len(instr.Operands))
|
return nil, fmt.Errorf("%s expects 1 operand, got %d", mnem, len(instr.Operands))
|
||||||
}
|
}
|
||||||
@@ -533,6 +527,14 @@ func encodeLOONG64Branch(instr *ast.Instr, mnem string, pc int, offsets map[stri
|
|||||||
}
|
}
|
||||||
return l64wordLE(l64irr16(l64branchTable["JIRL"], 0, rj, rd)), nil
|
return l64wordLE(l64irr16(l64branchTable["JIRL"], 0, rj, rd)), nil
|
||||||
}
|
}
|
||||||
|
// Direct symbol: sym+off(SB) → bl with an R_CALLLOONG64 relocation (the
|
||||||
|
// linker fills the offset), as the toolchain does for CALL/BL/JAL.
|
||||||
|
if op.Addr.Sym != nil && op.Addr.Sym.Pseudo == "SB" && link {
|
||||||
|
if relocs != nil {
|
||||||
|
*relocs = append(*relocs, Reloc{Off: 0, After: 4, Name: op.Addr.Sym.Name, Kind: RelLoong64Branch, Addend: op.Addr.Sym.Offset})
|
||||||
|
}
|
||||||
|
return l64wordLE(l64bbl(l64jumpTable["BL"], 0)), nil
|
||||||
|
}
|
||||||
// Direct: label → b/bl.
|
// Direct: label → b/bl.
|
||||||
target := resolve(l64Label(op))
|
target := resolve(l64Label(op))
|
||||||
targetOff, ok := offsets[target]
|
targetOff, ok := offsets[target]
|
||||||
|
|||||||
+139
-34
@@ -20,14 +20,20 @@ import (
|
|||||||
// (the toolchain aligns frames with `if autosize&4 != 0 { autosize += 4 }`).
|
// (the toolchain aligns frames with `if autosize&4 != 0 { autosize += 4 }`).
|
||||||
// A leaf function (no calls) with a zero frame gets no prologue at all.
|
// A leaf function (no calls) with a zero frame gets no prologue at all.
|
||||||
//
|
//
|
||||||
// Prologue (autosize > 0), byte-identical to the toolchain:
|
// Prologue (autosize > 0, small), byte-identical to the toolchain:
|
||||||
//
|
//
|
||||||
// MOVV R1, -autosize(R3) // save LR below the new SP (traceback-safe)
|
// MOVV R1, -autosize(R3) // save LR below the new SP (traceback-safe)
|
||||||
// ADDV $-autosize, R3 // open the frame
|
// ADDV $-autosize, R3 // open the frame
|
||||||
// MOVV R1, 0(R3) // save LR again at SP (signal-safety)
|
// MOVV R1, 0(R3) // save LR again at SP (signal-safety)
|
||||||
//
|
//
|
||||||
|
// Large frames (autosize past the 12-bit offset or immediate ranges) expand
|
||||||
|
// the store and the adjust through REGTMP (R30) exactly as the toolchain's
|
||||||
|
// assembler does: the store via the rounding LU12IW split, the adjust via
|
||||||
|
// the floor LU12IW/ORI split.
|
||||||
|
//
|
||||||
// Epilogue: MOVV 0(R3), R1; ADDV $autosize, R3 (non-leaf only for the LR
|
// Epilogue: MOVV 0(R3), R1; ADDV $autosize, R3 (non-leaf only for the LR
|
||||||
// restore); the RET's jirl r0, r1, 0 follows.
|
// restore; the adjust materialised when the immediate does not fit); the
|
||||||
|
// RET's jirl r0, r1, 0 follows.
|
||||||
|
|
||||||
// loong64FrameInfo holds the frame layout derived from a TEXT directive.
|
// loong64FrameInfo holds the frame layout derived from a TEXT directive.
|
||||||
type loong64FrameInfo struct {
|
type loong64FrameInfo struct {
|
||||||
@@ -84,62 +90,108 @@ func loong64ComputeFrame(t *ast.Text) loong64FrameInfo {
|
|||||||
|
|
||||||
// loong64GuardLen returns the byte length of the stack-split guard prefix
|
// loong64GuardLen returns the byte length of the stack-split guard prefix
|
||||||
// (zero when the function needs no guard). The big class materialises two
|
// (zero when the function needs no guard). The big class materialises two
|
||||||
// constants through R30.
|
// constants through R30; each materialisation shrinks by one word when the
|
||||||
|
// constant's low 12 bits are zero.
|
||||||
func loong64GuardLen(fi loong64FrameInfo) int {
|
func loong64GuardLen(fi loong64FrameInfo) int {
|
||||||
if !fi.needSplit {
|
if !fi.needSplit {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
off := int64(fi.autosize - stackSmall)
|
||||||
switch fi.splitClass {
|
switch fi.splitClass {
|
||||||
case 0:
|
case 0:
|
||||||
return 12
|
return 12
|
||||||
case 1:
|
case 1:
|
||||||
return 16
|
if off <= 2048 {
|
||||||
|
return 16 // ADDV $-off fits the signed 12-bit immediate
|
||||||
|
}
|
||||||
|
return 24 // MOVV + LU12IW + ORI + ADDV + SGTU + BEQ
|
||||||
default:
|
default:
|
||||||
return 40 // MOVV + [LU12IW+ORI] + SGTU + BNE + [LU12IW+ORI] + ADDV + SGTU + BEQ
|
// MOVV + [mat] + SGTU + BNE + [mat] + ADDV + SGTU + BEQ
|
||||||
|
return (6 + loong64MatLen(off) + loong64MatLen(-off)) * 4
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// loong64Lu12iOri materialises the 32-bit constant v in rd with the
|
// loong64MatLen reports the word count of materialising v in R30: a value
|
||||||
// toolchain's LU12IW/ORI pair (the ORI reads and writes rd itself).
|
// with a zero high part needs only the ORI (the toolchain's MOVW $v, R30),
|
||||||
func loong64Lu12iOri(rd int, v int64) []uint32 {
|
// one with a zero low part only the LU12IW.
|
||||||
hi := int32(v >> 12)
|
func loong64MatLen(v int64) int {
|
||||||
lo := int32(v & 0xFFF)
|
if v>>12 == 0 || v&0xFFF == 0 {
|
||||||
return []uint32{
|
return 1
|
||||||
0x0a<<25 | uint32(hi&0xFFFFF)<<5 | uint32(rd),
|
|
||||||
0x0e<<22 | uint32(lo)<<10 | uint32(rd)<<5 | uint32(rd),
|
|
||||||
}
|
}
|
||||||
|
return 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// loong64MatWords appends the words that materialise v in R30, splitting it
|
||||||
|
// as v>>12 plus the zero-extended low 12 bits.
|
||||||
|
func loong64MatWords(ws []uint32, v int64) []uint32 {
|
||||||
|
hi := v >> 12
|
||||||
|
lo := v & 0xFFF
|
||||||
|
if hi == 0 {
|
||||||
|
return append(ws, l64irr(l64OriOp, int(v), 0, 30))
|
||||||
|
}
|
||||||
|
ws = append(ws, l64ir(l64Lu12iwOp, int(hi), 30))
|
||||||
|
if lo != 0 {
|
||||||
|
ws = append(ws, l64irr(l64OriOp, int(lo), 30, 30))
|
||||||
|
}
|
||||||
|
return ws
|
||||||
|
}
|
||||||
|
|
||||||
|
// The LU12IW and ORI opcode bases (2RI20 and 2RI12 formats); the ORI reads
|
||||||
|
// and writes rd itself.
|
||||||
|
const (
|
||||||
|
l64Lu12iwOp = 0x0a << 25
|
||||||
|
l64OriOp = 0x0e << 22
|
||||||
|
)
|
||||||
|
|
||||||
|
// loong64Imm12 reports whether v fits a signed 12-bit immediate.
|
||||||
|
func loong64Imm12(v int64) bool { return v >= -2048 && v <= 2047 }
|
||||||
|
|
||||||
// loong64GuardBytes emits the stack-split guard prefix. blockStart is the
|
// loong64GuardBytes emits the stack-split guard prefix. blockStart is the
|
||||||
// function-relative address of the morestack call at the end of the function;
|
// function-relative address of the morestack call at the end of the function;
|
||||||
// branch displacements are in instructions.
|
// branch displacements are in instructions and are computed from each
|
||||||
|
// branch's own position.
|
||||||
func loong64GuardBytes(fi loong64FrameInfo, blockStart int) []byte {
|
func loong64GuardBytes(fi loong64FrameInfo, blockStart int) []byte {
|
||||||
// MOVV 16(g), R20 (g.stackguard0), g = R22.
|
// MOVV 16(g), R20 (g.stackguard0), g = R22.
|
||||||
ws := []uint32{l64irr(l64loadStoreTable["MOVV"].ld, 16, 22, 20)}
|
ws := []uint32{l64irr(l64loadStoreTable["MOVV"].ld, 16, 22, 20)}
|
||||||
|
off := int64(fi.autosize - stackSmall)
|
||||||
|
// beq appends BEQ R20, blockStart from the branch's own position.
|
||||||
|
beq := func() {
|
||||||
|
ws = append(ws, loong64Beqz(20, int32((blockStart-len(ws)*4)>>2)))
|
||||||
|
}
|
||||||
switch fi.splitClass {
|
switch fi.splitClass {
|
||||||
case 0:
|
case 0:
|
||||||
// SGTU SP, R20, R20; BEQ R20, more
|
// SGTU SP, R20, R20; BEQ R20, more
|
||||||
ws = append(ws, l64rrr(l64DualTable["SGTU"].rrr, 3, 20, 20))
|
ws = append(ws, l64rrr(l64DualTable["SGTU"].rrr, 3, 20, 20))
|
||||||
ws = append(ws, loong64Beqz(20, int32((blockStart-8)>>2)))
|
beq()
|
||||||
case 1:
|
case 1:
|
||||||
off := int32(fi.autosize - stackSmall)
|
ws = append(ws, loong64MediumWords(off)...)
|
||||||
ws = append(ws, l64irr(l64DualTable["ADDV"].imm, int(-off), 3, 24))
|
|
||||||
ws = append(ws, l64rrr(l64DualTable["SGTU"].rrr, 24, 20, 20))
|
ws = append(ws, l64rrr(l64DualTable["SGTU"].rrr, 24, 20, 20))
|
||||||
ws = append(ws, loong64Beqz(20, int32((blockStart-12)>>2)))
|
beq()
|
||||||
default:
|
default:
|
||||||
off := int64(fi.autosize - stackSmall)
|
// SGTU $off, SP, R24 catches the SP underflow a huge frame would
|
||||||
movLen := 8 // LU12IW + ORI
|
// cause; BNE jumps to morestack in that case.
|
||||||
ws = append(ws, loong64Lu12iOri(30, off)...)
|
ws = append(ws, loong64MatWords(nil, off)...)
|
||||||
ws = append(ws, l64rrr(l64DualTable["SGTU"].rrr, 30, 3, 24))
|
ws = append(ws, l64rrr(l64DualTable["SGTU"].rrr, 30, 3, 24))
|
||||||
ws = append(ws, loong64Bnez(24, int32((blockStart-(8+movLen))>>2)))
|
ws = append(ws, loong64Bnez(24, int32((blockStart-len(ws)*4)>>2)))
|
||||||
ws = append(ws, loong64Lu12iOri(30, -off)...)
|
ws = append(ws, loong64MatWords(nil, -off)...)
|
||||||
ws = append(ws, l64rrr(l64DualTable["ADDV"].rrr, 30, 3, 24))
|
ws = append(ws, l64rrr(l64DualTable["ADDV"].rrr, 30, 3, 24))
|
||||||
ws = append(ws, l64rrr(l64DualTable["SGTU"].rrr, 24, 20, 20))
|
ws = append(ws, l64rrr(l64DualTable["SGTU"].rrr, 24, 20, 20))
|
||||||
ws = append(ws, loong64Beqz(20, int32((blockStart-loong64GuardLen(fi)+12)>>2)))
|
beq()
|
||||||
}
|
}
|
||||||
return l64WordsLE(ws...)
|
return l64WordsLE(ws...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// loong64MediumWords emits the medium-class stack check for offset off: the
|
||||||
|
// ADDV immediate when it fits, otherwise the same sequence with the constant
|
||||||
|
// materialised in R30.
|
||||||
|
func loong64MediumWords(off int64) []uint32 {
|
||||||
|
if off <= 2048 {
|
||||||
|
return []uint32{l64irr(l64DualTable["ADDV"].imm, int(-off), 3, 24)}
|
||||||
|
}
|
||||||
|
ws := loong64MatWords(nil, -off)
|
||||||
|
return append(ws, l64rrr(l64DualTable["ADDV"].rrr, 30, 3, 24))
|
||||||
|
}
|
||||||
|
|
||||||
// loong64Beqz/loong64Bnez build the 21-bit conditional branches against R0
|
// loong64Beqz/loong64Bnez build the 21-bit conditional branches against R0
|
||||||
// that the toolchain emits for its guard compares.
|
// that the toolchain emits for its guard compares.
|
||||||
func loong64Beqz(rj int, dispInstr int32) uint32 {
|
func loong64Beqz(rj int, dispInstr int32) uint32 {
|
||||||
@@ -150,11 +202,12 @@ func loong64Bnez(rj int, dispInstr int32) uint32 {
|
|||||||
return l64ir21(l64branch21Table["BNEZ"], int(dispInstr), rj)
|
return l64ir21(l64branch21Table["BNEZ"], int(dispInstr), rj)
|
||||||
}
|
}
|
||||||
|
|
||||||
// loong64MoreStackBlock emits the trailing block: MOVV R1, R31 (save LR),
|
// loong64MoreStackBlock emits the trailing block: MOVV R1, R31 (save LR, the
|
||||||
// BL runtime.morestack_noctxt, B back to the function entry.
|
// toolchain's OR R1, R0, R31 expansion), BL runtime.morestack_noctxt, B back
|
||||||
|
// to the function entry.
|
||||||
func loong64MoreStackBlock(blockStart int) ([]byte, Reloc) {
|
func loong64MoreStackBlock(blockStart int) ([]byte, Reloc) {
|
||||||
ws := []uint32{
|
ws := []uint32{
|
||||||
l64rrr(l64DualTable["ADD"].rrr, 0, 1, 31), // MOVV R1, R31 (ADD R1, R0, R31)
|
l64rrr(l64DualTable["OR"].rrr, 0, 1, 31), // MOVV R1, R31 (OR R1, R0, R31)
|
||||||
l64bbl(l64jumpTable["BL"], 0), // BL, patched by the linker
|
l64bbl(l64jumpTable["BL"], 0), // BL, patched by the linker
|
||||||
}
|
}
|
||||||
disp := (-(blockStart + 8)) >> 2
|
disp := (-(blockStart + 8)) >> 2
|
||||||
@@ -185,17 +238,37 @@ func loong64IsLeaf(t *ast.Text) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// loong64Prologue returns the prologue bytes for a loong64 function.
|
// loong64Prologue returns the prologue bytes for a loong64 function. When
|
||||||
|
// the LR store offset leaves the toolchain's 12-bit store range ([-2046,
|
||||||
|
// 2045], BIG_12 = 2046) or the SP adjust immediate its 12-bit immediate
|
||||||
|
// range, each switches to the R30 materialisation the assembler expands it
|
||||||
|
// to: the store uses the rounding %hi/%lo split (LU12IW of (v+2048)>>12,
|
||||||
|
// REGTMP += SP, store at the raw offset), the adjust the floor split
|
||||||
|
// (LU12IW, ORI when the low part is non-zero, REGTMP += SP).
|
||||||
func loong64Prologue(fi loong64FrameInfo) []byte {
|
func loong64Prologue(fi loong64FrameInfo) []byte {
|
||||||
if fi.autosize == 0 {
|
if fi.autosize == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
addiD := l64DualTable["ADDV"].imm
|
addiD := l64DualTable["ADDV"].imm
|
||||||
return l64WordsLE(
|
var ws []uint32
|
||||||
l64irr(l64loadStoreTable["MOVV"].st, -fi.autosize, 3, 1), // MOVV R1, -autosize(R3)
|
storeBase := 3
|
||||||
l64irr(addiD, -fi.autosize, 3, 3), // ADDV $-autosize, R3
|
if fi.autosize > 2046 {
|
||||||
l64irr(l64loadStoreTable["MOVV"].st, 0, 3, 1), // MOVV R1, 0(R3)
|
// The store goes through REGTMP: LU12IW of the rounding split,
|
||||||
)
|
// REGTMP += SP, then the store at REGTMP with the truncated offset.
|
||||||
|
v := -int64(fi.autosize)
|
||||||
|
ws = append(ws, l64ir(l64Lu12iwOp, int((v+2048)>>12), 30))
|
||||||
|
ws = append(ws, l64rrr(l64DualTable["ADDV"].rrr, 3, 30, 30))
|
||||||
|
storeBase = 30
|
||||||
|
}
|
||||||
|
ws = append(ws, l64irr(l64loadStoreTable["MOVV"].st, -fi.autosize, storeBase, 1)) // MOVV R1, -autosize(base)
|
||||||
|
if loong64Imm12(-int64(fi.autosize)) {
|
||||||
|
ws = append(ws, l64irr(addiD, -fi.autosize, 3, 3)) // ADDV $-autosize, R3
|
||||||
|
} else {
|
||||||
|
ws = append(ws, loong64MatWords(nil, -int64(fi.autosize))...)
|
||||||
|
ws = append(ws, l64rrr(l64DualTable["ADDV"].rrr, 30, 3, 3))
|
||||||
|
}
|
||||||
|
ws = append(ws, l64irr(l64loadStoreTable["MOVV"].st, 0, 3, 1)) // MOVV R1, 0(R3)
|
||||||
|
return l64WordsLE(ws...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// loong64Return returns the bytes for a RET: the epilogue (restore LR and
|
// loong64Return returns the bytes for a RET: the epilogue (restore LR and
|
||||||
@@ -207,14 +280,46 @@ func loong64Return(fi loong64FrameInfo) []byte {
|
|||||||
// MOVV 0(R3), R1, restore the link register.
|
// MOVV 0(R3), R1, restore the link register.
|
||||||
ws = append(ws, l64irr(l64loadStoreTable["MOVV"].ld, 0, 3, 1))
|
ws = append(ws, l64irr(l64loadStoreTable["MOVV"].ld, 0, 3, 1))
|
||||||
}
|
}
|
||||||
// ADDV $autosize, R3, close the frame.
|
// ADDV $autosize, R3, close the frame (materialised when the
|
||||||
|
// immediate does not fit).
|
||||||
|
if loong64Imm12(int64(fi.autosize)) {
|
||||||
ws = append(ws, l64irr(l64DualTable["ADDV"].imm, fi.autosize, 3, 3))
|
ws = append(ws, l64irr(l64DualTable["ADDV"].imm, fi.autosize, 3, 3))
|
||||||
|
} else {
|
||||||
|
ws = append(ws, loong64MatWords(nil, int64(fi.autosize))...)
|
||||||
|
ws = append(ws, l64rrr(l64DualTable["ADDV"].rrr, 30, 3, 3))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// jirl r0, r1, 0, return.
|
// jirl r0, r1, 0, return.
|
||||||
ws = append(ws, l64irr16(l64branchTable["JIRL"], 0, 1, 0))
|
ws = append(ws, l64irr16(l64branchTable["JIRL"], 0, 1, 0))
|
||||||
return l64WordsLE(ws...)
|
return l64WordsLE(ws...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// loong64StoreWords reports the prologue word count of the LR store, and
|
||||||
|
// loong64AdjustWords the word count of an SP adjust of v: the immediate
|
||||||
|
// forms when they fit, otherwise the R30 materialisation sequences.
|
||||||
|
func loong64StoreWords(autosize int) int {
|
||||||
|
if autosize > 2046 {
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func loong64AdjustWords(v int64) int {
|
||||||
|
if loong64Imm12(v) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return loong64MatLen(v) + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// loong64EpilogueWords reports the epilogue word count the RET expands to.
|
||||||
|
func loong64EpilogueWords(fi loong64FrameInfo) int {
|
||||||
|
n := loong64AdjustWords(int64(fi.autosize))
|
||||||
|
if !fi.leaf {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
// loong64ResolvePseudo translates a pseudo-register memory reference into a
|
// loong64ResolvePseudo translates a pseudo-register memory reference into a
|
||||||
// hardware base register and offset. x+N(FP) → (N + autosize + 8)(SP);
|
// hardware base register and offset. x+N(FP) → (N + autosize + 8)(SP);
|
||||||
// x-N(SP) → (autosize - N)(SP). Returns base = -1 for an unresolvable
|
// x-N(SP) → (autosize - N)(SP). Returns base = -1 for an unresolvable
|
||||||
|
|||||||
Reference in New Issue
Block a user