podman
784 строки · 20.2 Кб
1// Derived from Inferno utils/5c/swt.c
2// https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5c/swt.c
3//
4// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
5// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
6// Portions Copyright © 1997-1999 Vita Nuova Limited
7// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
8// Portions Copyright © 2004,2006 Bruce Ellis
9// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
10// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
11// Portions Copyright © 2009 The Go Authors. All rights reserved.
12//
13// Permission is hereby granted, free of charge, to any person obtaining a copy
14// of this software and associated documentation files (the "Software"), to deal
15// in the Software without restriction, including without limitation the rights
16// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17// copies of the Software, and to permit persons to whom the Software is
18// furnished to do so, subject to the following conditions:
19//
20// The above copyright notice and this permission notice shall be included in
21// all copies or substantial portions of the Software.
22//
23// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
29// THE SOFTWARE.
30
31package arm32
33import (34"github.com/twitchyliquid64/golang-asm/obj"35"github.com/twitchyliquid64/golang-asm/objabi"36"github.com/twitchyliquid64/golang-asm/sys"37)
38
39var progedit_tlsfallback *obj.LSym40
41func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {42p.From.Class = 043p.To.Class = 044
45c := ctxt5{ctxt: ctxt, newprog: newprog}46
47// Rewrite B/BL to symbol as TYPE_BRANCH.48switch p.As {49case AB, ABL, obj.ADUFFZERO, obj.ADUFFCOPY:50if p.To.Type == obj.TYPE_MEM && (p.To.Name == obj.NAME_EXTERN || p.To.Name == obj.NAME_STATIC) && p.To.Sym != nil {51p.To.Type = obj.TYPE_BRANCH52}53}54
55// Replace TLS register fetches on older ARM processors.56switch p.As {57// Treat MRC 15, 0, <reg>, C13, C0, 3 specially.58case AMRC:59if p.To.Offset&0xffff0fff == 0xee1d0f70 {60// Because the instruction might be rewritten to a BL which returns in R061// the register must be zero.62if p.To.Offset&0xf000 != 0 {63ctxt.Diag("%v: TLS MRC instruction must write to R0 as it might get translated into a BL instruction", p.Line())64}65
66if objabi.GOARM < 7 {67// Replace it with BL runtime.read_tls_fallback(SB) for ARM CPUs that lack the tls extension.68if progedit_tlsfallback == nil {69progedit_tlsfallback = ctxt.Lookup("runtime.read_tls_fallback")70}71
72// MOVW LR, R1173p.As = AMOVW74
75p.From.Type = obj.TYPE_REG76p.From.Reg = REGLINK77p.To.Type = obj.TYPE_REG78p.To.Reg = REGTMP79
80// BL runtime.read_tls_fallback(SB)81p = obj.Appendp(p, newprog)82
83p.As = ABL84p.To.Type = obj.TYPE_BRANCH85p.To.Sym = progedit_tlsfallback86p.To.Offset = 087
88// MOVW R11, LR89p = obj.Appendp(p, newprog)90
91p.As = AMOVW92p.From.Type = obj.TYPE_REG93p.From.Reg = REGTMP94p.To.Type = obj.TYPE_REG95p.To.Reg = REGLINK96break97}98}99
100// Otherwise, MRC/MCR instructions need no further treatment.101p.As = AWORD102}103
104// Rewrite float constants to values stored in memory.105switch p.As {106case AMOVF:107if p.From.Type == obj.TYPE_FCONST && c.chipfloat5(p.From.Val.(float64)) < 0 && (c.chipzero5(p.From.Val.(float64)) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) {108f32 := float32(p.From.Val.(float64))109p.From.Type = obj.TYPE_MEM110p.From.Sym = ctxt.Float32Sym(f32)111p.From.Name = obj.NAME_EXTERN112p.From.Offset = 0113}114
115case AMOVD:116if p.From.Type == obj.TYPE_FCONST && c.chipfloat5(p.From.Val.(float64)) < 0 && (c.chipzero5(p.From.Val.(float64)) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) {117p.From.Type = obj.TYPE_MEM118p.From.Sym = ctxt.Float64Sym(p.From.Val.(float64))119p.From.Name = obj.NAME_EXTERN120p.From.Offset = 0121}122}123
124if ctxt.Flag_dynlink {125c.rewriteToUseGot(p)126}127}
128
129// Rewrite p, if necessary, to access global data via the global offset table.
130func (c *ctxt5) rewriteToUseGot(p *obj.Prog) {131if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {132// ADUFFxxx $offset133// becomes134// MOVW runtime.duffxxx@GOT, R9135// ADD $offset, R9136// CALL (R9)137var sym *obj.LSym138if p.As == obj.ADUFFZERO {139sym = c.ctxt.Lookup("runtime.duffzero")140} else {141sym = c.ctxt.Lookup("runtime.duffcopy")142}143offset := p.To.Offset144p.As = AMOVW145p.From.Type = obj.TYPE_MEM146p.From.Name = obj.NAME_GOTREF147p.From.Sym = sym148p.To.Type = obj.TYPE_REG149p.To.Reg = REG_R9150p.To.Name = obj.NAME_NONE151p.To.Offset = 0152p.To.Sym = nil153p1 := obj.Appendp(p, c.newprog)154p1.As = AADD155p1.From.Type = obj.TYPE_CONST156p1.From.Offset = offset157p1.To.Type = obj.TYPE_REG158p1.To.Reg = REG_R9159p2 := obj.Appendp(p1, c.newprog)160p2.As = obj.ACALL161p2.To.Type = obj.TYPE_MEM162p2.To.Reg = REG_R9163return164}165
166// We only care about global data: NAME_EXTERN means a global167// symbol in the Go sense, and p.Sym.Local is true for a few168// internally defined symbols.169if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {170// MOVW $sym, Rx becomes MOVW sym@GOT, Rx171// MOVW $sym+<off>, Rx becomes MOVW sym@GOT, Rx; ADD <off>, Rx172if p.As != AMOVW {173c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)174}175if p.To.Type != obj.TYPE_REG {176c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)177}178p.From.Type = obj.TYPE_MEM179p.From.Name = obj.NAME_GOTREF180if p.From.Offset != 0 {181q := obj.Appendp(p, c.newprog)182q.As = AADD183q.From.Type = obj.TYPE_CONST184q.From.Offset = p.From.Offset185q.To = p.To186p.From.Offset = 0187}188}189if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {190c.ctxt.Diag("don't know how to handle %v with -dynlink", p)191}192var source *obj.Addr193// MOVx sym, Ry becomes MOVW sym@GOT, R9; MOVx (R9), Ry194// MOVx Ry, sym becomes MOVW sym@GOT, R9; MOVx Ry, (R9)195// An addition may be inserted between the two MOVs if there is an offset.196if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {197if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {198c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)199}200source = &p.From201} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {202source = &p.To203} else {204return205}206if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {207return208}209if source.Sym.Type == objabi.STLSBSS {210return211}212if source.Type != obj.TYPE_MEM {213c.ctxt.Diag("don't know how to handle %v with -dynlink", p)214}215p1 := obj.Appendp(p, c.newprog)216p2 := obj.Appendp(p1, c.newprog)217
218p1.As = AMOVW219p1.From.Type = obj.TYPE_MEM220p1.From.Sym = source.Sym221p1.From.Name = obj.NAME_GOTREF222p1.To.Type = obj.TYPE_REG223p1.To.Reg = REG_R9224
225p2.As = p.As226p2.From = p.From227p2.To = p.To228if p.From.Name == obj.NAME_EXTERN {229p2.From.Reg = REG_R9230p2.From.Name = obj.NAME_NONE231p2.From.Sym = nil232} else if p.To.Name == obj.NAME_EXTERN {233p2.To.Reg = REG_R9234p2.To.Name = obj.NAME_NONE235p2.To.Sym = nil236} else {237return238}239obj.Nopout(p)240}
241
242// Prog.mark
243const (244FOLL = 1 << 0245LABEL = 1 << 1246LEAF = 1 << 2247)
248
249func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {250autosize := int32(0)251
252if cursym.Func.Text == nil || cursym.Func.Text.Link == nil {253return254}255
256c := ctxt5{ctxt: ctxt, cursym: cursym, newprog: newprog}257
258p := c.cursym.Func.Text259autoffset := int32(p.To.Offset)260if autoffset == -4 {261// Historical way to mark NOFRAME.262p.From.Sym.Set(obj.AttrNoFrame, true)263autoffset = 0264}265if autoffset < 0 || autoffset%4 != 0 {266c.ctxt.Diag("frame size %d not 0 or a positive multiple of 4", autoffset)267}268if p.From.Sym.NoFrame() {269if autoffset != 0 {270c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", autoffset)271}272}273
274cursym.Func.Locals = autoffset275cursym.Func.Args = p.To.Val.(int32)276
277/*278* find leaf subroutines
279*/
280for p := cursym.Func.Text; p != nil; p = p.Link {281switch p.As {282case obj.ATEXT:283p.Mark |= LEAF284
285case ADIV, ADIVU, AMOD, AMODU:286cursym.Func.Text.Mark &^= LEAF287
288case ABL,289ABX,290obj.ADUFFZERO,291obj.ADUFFCOPY:292cursym.Func.Text.Mark &^= LEAF293}294}295
296var q2 *obj.Prog297for p := cursym.Func.Text; p != nil; p = p.Link {298o := p.As299switch o {300case obj.ATEXT:301autosize = autoffset302
303if p.Mark&LEAF != 0 && autosize == 0 {304// A leaf function with no locals has no frame.305p.From.Sym.Set(obj.AttrNoFrame, true)306}307
308if !p.From.Sym.NoFrame() {309// If there is a stack frame at all, it includes310// space to save the LR.311autosize += 4312}313
314if autosize == 0 && cursym.Func.Text.Mark&LEAF == 0 {315// A very few functions that do not return to their caller316// are not identified as leaves but still have no frame.317if ctxt.Debugvlog {318ctxt.Logf("save suppressed in: %s\n", cursym.Name)319}320
321cursym.Func.Text.Mark |= LEAF322}323
324// FP offsets need an updated p.To.Offset.325p.To.Offset = int64(autosize) - 4326
327if cursym.Func.Text.Mark&LEAF != 0 {328cursym.Set(obj.AttrLeaf, true)329if p.From.Sym.NoFrame() {330break331}332}333
334if !p.From.Sym.NoSplit() {335p = c.stacksplit(p, autosize) // emit split check336}337
338// MOVW.W R14,$-autosize(SP)339p = obj.Appendp(p, c.newprog)340
341p.As = AMOVW342p.Scond |= C_WBIT343p.From.Type = obj.TYPE_REG344p.From.Reg = REGLINK345p.To.Type = obj.TYPE_MEM346p.To.Offset = int64(-autosize)347p.To.Reg = REGSP348p.Spadj = autosize349
350if cursym.Func.Text.From.Sym.Wrapper() {351// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame352//353// MOVW g_panic(g), R1354// CMP $0, R1355// B.NE checkargp356// end:357// NOP358// ... function ...359// checkargp:360// MOVW panic_argp(R1), R2361// ADD $(autosize+4), R13, R3362// CMP R2, R3363// B.NE end364// ADD $4, R13, R4365// MOVW R4, panic_argp(R1)366// B end367//368// The NOP is needed to give the jumps somewhere to land.369// It is a liblink NOP, not an ARM NOP: it encodes to 0 instruction bytes.370
371p = obj.Appendp(p, newprog)372p.As = AMOVW373p.From.Type = obj.TYPE_MEM374p.From.Reg = REGG375p.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic376p.To.Type = obj.TYPE_REG377p.To.Reg = REG_R1378
379p = obj.Appendp(p, newprog)380p.As = ACMP381p.From.Type = obj.TYPE_CONST382p.From.Offset = 0383p.Reg = REG_R1384
385// B.NE checkargp386bne := obj.Appendp(p, newprog)387bne.As = ABNE388bne.To.Type = obj.TYPE_BRANCH389
390// end: NOP391end := obj.Appendp(bne, newprog)392end.As = obj.ANOP393
394// find end of function395var last *obj.Prog396for last = end; last.Link != nil; last = last.Link {397}398
399// MOVW panic_argp(R1), R2400mov := obj.Appendp(last, newprog)401mov.As = AMOVW402mov.From.Type = obj.TYPE_MEM403mov.From.Reg = REG_R1404mov.From.Offset = 0 // Panic.argp405mov.To.Type = obj.TYPE_REG406mov.To.Reg = REG_R2407
408// B.NE branch target is MOVW above409bne.To.SetTarget(mov)410
411// ADD $(autosize+4), R13, R3412p = obj.Appendp(mov, newprog)413p.As = AADD414p.From.Type = obj.TYPE_CONST415p.From.Offset = int64(autosize) + 4416p.Reg = REG_R13417p.To.Type = obj.TYPE_REG418p.To.Reg = REG_R3419
420// CMP R2, R3421p = obj.Appendp(p, newprog)422p.As = ACMP423p.From.Type = obj.TYPE_REG424p.From.Reg = REG_R2425p.Reg = REG_R3426
427// B.NE end428p = obj.Appendp(p, newprog)429p.As = ABNE430p.To.Type = obj.TYPE_BRANCH431p.To.SetTarget(end)432
433// ADD $4, R13, R4434p = obj.Appendp(p, newprog)435p.As = AADD436p.From.Type = obj.TYPE_CONST437p.From.Offset = 4438p.Reg = REG_R13439p.To.Type = obj.TYPE_REG440p.To.Reg = REG_R4441
442// MOVW R4, panic_argp(R1)443p = obj.Appendp(p, newprog)444p.As = AMOVW445p.From.Type = obj.TYPE_REG446p.From.Reg = REG_R4447p.To.Type = obj.TYPE_MEM448p.To.Reg = REG_R1449p.To.Offset = 0 // Panic.argp450
451// B end452p = obj.Appendp(p, newprog)453p.As = AB454p.To.Type = obj.TYPE_BRANCH455p.To.SetTarget(end)456
457// reset for subsequent passes458p = end459}460
461case obj.ARET:462nocache(p)463if cursym.Func.Text.Mark&LEAF != 0 {464if autosize == 0 {465p.As = AB466p.From = obj.Addr{}467if p.To.Sym != nil { // retjmp468p.To.Type = obj.TYPE_BRANCH469} else {470p.To.Type = obj.TYPE_MEM471p.To.Offset = 0472p.To.Reg = REGLINK473}474
475break476}477}478
479p.As = AMOVW480p.Scond |= C_PBIT481p.From.Type = obj.TYPE_MEM482p.From.Offset = int64(autosize)483p.From.Reg = REGSP484p.To.Type = obj.TYPE_REG485p.To.Reg = REGPC486
487// If there are instructions following488// this ARET, they come from a branch489// with the same stackframe, so no spadj.490if p.To.Sym != nil { // retjmp491p.To.Reg = REGLINK492q2 = obj.Appendp(p, newprog)493q2.As = AB494q2.To.Type = obj.TYPE_BRANCH495q2.To.Sym = p.To.Sym496p.To.Sym = nil497p = q2498}499
500case AADD:501if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {502p.Spadj = int32(-p.From.Offset)503}504
505case ASUB:506if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {507p.Spadj = int32(p.From.Offset)508}509
510case ADIV, ADIVU, AMOD, AMODU:511if cursym.Func.Text.From.Sym.NoSplit() {512ctxt.Diag("cannot divide in NOSPLIT function")513}514const debugdivmod = false515if debugdivmod {516break517}518if p.From.Type != obj.TYPE_REG {519break520}521if p.To.Type != obj.TYPE_REG {522break523}524
525// Make copy because we overwrite p below.526q1 := *p527if q1.Reg == REGTMP || q1.Reg == 0 && q1.To.Reg == REGTMP {528ctxt.Diag("div already using REGTMP: %v", p)529}530
531/* MOV m(g),REGTMP */532p.As = AMOVW533p.Pos = q1.Pos534p.From.Type = obj.TYPE_MEM535p.From.Reg = REGG536p.From.Offset = 6 * 4 // offset of g.m537p.Reg = 0538p.To.Type = obj.TYPE_REG539p.To.Reg = REGTMP540
541/* MOV a,m_divmod(REGTMP) */542p = obj.Appendp(p, newprog)543p.As = AMOVW544p.Pos = q1.Pos545p.From.Type = obj.TYPE_REG546p.From.Reg = q1.From.Reg547p.To.Type = obj.TYPE_MEM548p.To.Reg = REGTMP549p.To.Offset = 8 * 4 // offset of m.divmod550
551/* MOV b, R8 */552p = obj.Appendp(p, newprog)553p.As = AMOVW554p.Pos = q1.Pos555p.From.Type = obj.TYPE_REG556p.From.Reg = q1.Reg557if q1.Reg == 0 {558p.From.Reg = q1.To.Reg559}560p.To.Type = obj.TYPE_REG561p.To.Reg = REG_R8562p.To.Offset = 0563
564/* CALL appropriate */565p = obj.Appendp(p, newprog)566p.As = ABL567p.Pos = q1.Pos568p.To.Type = obj.TYPE_BRANCH569switch o {570case ADIV:571p.To.Sym = symdiv572case ADIVU:573p.To.Sym = symdivu574case AMOD:575p.To.Sym = symmod576case AMODU:577p.To.Sym = symmodu578}579
580/* MOV REGTMP, b */581p = obj.Appendp(p, newprog)582p.As = AMOVW583p.Pos = q1.Pos584p.From.Type = obj.TYPE_REG585p.From.Reg = REGTMP586p.From.Offset = 0587p.To.Type = obj.TYPE_REG588p.To.Reg = q1.To.Reg589
590case AMOVW:591if (p.Scond&C_WBIT != 0) && p.To.Type == obj.TYPE_MEM && p.To.Reg == REGSP {592p.Spadj = int32(-p.To.Offset)593}594if (p.Scond&C_PBIT != 0) && p.From.Type == obj.TYPE_MEM && p.From.Reg == REGSP && p.To.Reg != REGPC {595p.Spadj = int32(-p.From.Offset)596}597if p.From.Type == obj.TYPE_ADDR && p.From.Reg == REGSP && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {598p.Spadj = int32(-p.From.Offset)599}600
601case obj.AGETCALLERPC:602if cursym.Leaf() {603/* MOVW LR, Rd */604p.As = AMOVW605p.From.Type = obj.TYPE_REG606p.From.Reg = REGLINK607} else {608/* MOVW (RSP), Rd */609p.As = AMOVW610p.From.Type = obj.TYPE_MEM611p.From.Reg = REGSP612}613}614}615}
616
617func (c *ctxt5) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {618// MOVW g_stackguard(g), R1619p = obj.Appendp(p, c.newprog)620
621p.As = AMOVW622p.From.Type = obj.TYPE_MEM623p.From.Reg = REGG624p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0625if c.cursym.CFunc() {626p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1627}628p.To.Type = obj.TYPE_REG629p.To.Reg = REG_R1630
631// Mark the stack bound check and morestack call async nonpreemptible.632// If we get preempted here, when resumed the preemption request is633// cleared, but we'll still call morestack, which will double the stack634// unnecessarily. See issue #35470.635p = c.ctxt.StartUnsafePoint(p, c.newprog)636
637if framesize <= objabi.StackSmall {638// small stack: SP < stackguard639// CMP stackguard, SP640p = obj.Appendp(p, c.newprog)641
642p.As = ACMP643p.From.Type = obj.TYPE_REG644p.From.Reg = REG_R1645p.Reg = REGSP646} else if framesize <= objabi.StackBig {647// large stack: SP-framesize < stackguard-StackSmall648// MOVW $-(framesize-StackSmall)(SP), R2649// CMP stackguard, R2650p = obj.Appendp(p, c.newprog)651
652p.As = AMOVW653p.From.Type = obj.TYPE_ADDR654p.From.Reg = REGSP655p.From.Offset = -(int64(framesize) - objabi.StackSmall)656p.To.Type = obj.TYPE_REG657p.To.Reg = REG_R2658
659p = obj.Appendp(p, c.newprog)660p.As = ACMP661p.From.Type = obj.TYPE_REG662p.From.Reg = REG_R1663p.Reg = REG_R2664} else {665// Such a large stack we need to protect against wraparound666// if SP is close to zero.667// SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall)668// The +StackGuard on both sides is required to keep the left side positive:669// SP is allowed to be slightly below stackguard. See stack.h.670// CMP $StackPreempt, R1671// MOVW.NE $StackGuard(SP), R2672// SUB.NE R1, R2673// MOVW.NE $(framesize+(StackGuard-StackSmall)), R3674// CMP.NE R3, R2675p = obj.Appendp(p, c.newprog)676
677p.As = ACMP678p.From.Type = obj.TYPE_CONST679p.From.Offset = int64(uint32(objabi.StackPreempt & (1<<32 - 1)))680p.Reg = REG_R1681
682p = obj.Appendp(p, c.newprog)683p.As = AMOVW684p.From.Type = obj.TYPE_ADDR685p.From.Reg = REGSP686p.From.Offset = int64(objabi.StackGuard)687p.To.Type = obj.TYPE_REG688p.To.Reg = REG_R2689p.Scond = C_SCOND_NE690
691p = obj.Appendp(p, c.newprog)692p.As = ASUB693p.From.Type = obj.TYPE_REG694p.From.Reg = REG_R1695p.To.Type = obj.TYPE_REG696p.To.Reg = REG_R2697p.Scond = C_SCOND_NE698
699p = obj.Appendp(p, c.newprog)700p.As = AMOVW701p.From.Type = obj.TYPE_ADDR702p.From.Offset = int64(framesize) + (int64(objabi.StackGuard) - objabi.StackSmall)703p.To.Type = obj.TYPE_REG704p.To.Reg = REG_R3705p.Scond = C_SCOND_NE706
707p = obj.Appendp(p, c.newprog)708p.As = ACMP709p.From.Type = obj.TYPE_REG710p.From.Reg = REG_R3711p.Reg = REG_R2712p.Scond = C_SCOND_NE713}714
715// BLS call-to-morestack716bls := obj.Appendp(p, c.newprog)717bls.As = ABLS718bls.To.Type = obj.TYPE_BRANCH719
720end := c.ctxt.EndUnsafePoint(bls, c.newprog, -1)721
722var last *obj.Prog723for last = c.cursym.Func.Text; last.Link != nil; last = last.Link {724}725
726// Now we are at the end of the function, but logically727// we are still in function prologue. We need to fix the728// SP data and PCDATA.729spfix := obj.Appendp(last, c.newprog)730spfix.As = obj.ANOP731spfix.Spadj = -framesize732
733pcdata := c.ctxt.EmitEntryStackMap(c.cursym, spfix, c.newprog)734pcdata = c.ctxt.StartUnsafePoint(pcdata, c.newprog)735
736// MOVW LR, R3737movw := obj.Appendp(pcdata, c.newprog)738movw.As = AMOVW739movw.From.Type = obj.TYPE_REG740movw.From.Reg = REGLINK741movw.To.Type = obj.TYPE_REG742movw.To.Reg = REG_R3743
744bls.To.SetTarget(movw)745
746// BL runtime.morestack747call := obj.Appendp(movw, c.newprog)748call.As = obj.ACALL749call.To.Type = obj.TYPE_BRANCH750morestack := "runtime.morestack"751switch {752case c.cursym.CFunc():753morestack = "runtime.morestackc"754case !c.cursym.Func.Text.From.Sym.NeedCtxt():755morestack = "runtime.morestack_noctxt"756}757call.To.Sym = c.ctxt.Lookup(morestack)758
759pcdata = c.ctxt.EndUnsafePoint(call, c.newprog, -1)760
761// B start762b := obj.Appendp(pcdata, c.newprog)763b.As = obj.AJMP764b.To.Type = obj.TYPE_BRANCH765b.To.SetTarget(c.cursym.Func.Text.Link)766b.Spadj = +framesize767
768return end769}
770
771var unaryDst = map[obj.As]bool{772ASWI: true,773AWORD: true,774}
775
776var Linkarm = obj.LinkArch{777Arch: sys.ArchARM,778Init: buildop,779Preprocess: preprocess,780Assemble: span5,781Progedit: progedit,782UnaryDst: unaryDst,783DWARFRegisters: ARMDWARFRegisters,784}
785