obj5.go raw
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
31 package arm
32
33 import (
34 "github.com/twitchyliquid64/golang-asm/obj"
35 "github.com/twitchyliquid64/golang-asm/objabi"
36 "github.com/twitchyliquid64/golang-asm/sys"
37 )
38
39 var progedit_tlsfallback *obj.LSym
40
41 func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
42 p.From.Class = 0
43 p.To.Class = 0
44
45 c := ctxt5{ctxt: ctxt, newprog: newprog}
46
47 // Rewrite B/BL to symbol as TYPE_BRANCH.
48 switch p.As {
49 case AB, ABL, obj.ADUFFZERO, obj.ADUFFCOPY:
50 if p.To.Type == obj.TYPE_MEM && (p.To.Name == obj.NAME_EXTERN || p.To.Name == obj.NAME_STATIC) && p.To.Sym != nil {
51 p.To.Type = obj.TYPE_BRANCH
52 }
53 }
54
55 // Replace TLS register fetches on older ARM processors.
56 switch p.As {
57 // Treat MRC 15, 0, <reg>, C13, C0, 3 specially.
58 case AMRC:
59 if p.To.Offset&0xffff0fff == 0xee1d0f70 {
60 // Because the instruction might be rewritten to a BL which returns in R0
61 // the register must be zero.
62 if p.To.Offset&0xf000 != 0 {
63 ctxt.Diag("%v: TLS MRC instruction must write to R0 as it might get translated into a BL instruction", p.Line())
64 }
65
66 if objabi.GOARM < 7 {
67 // Replace it with BL runtime.read_tls_fallback(SB) for ARM CPUs that lack the tls extension.
68 if progedit_tlsfallback == nil {
69 progedit_tlsfallback = ctxt.Lookup("runtime.read_tls_fallback")
70 }
71
72 // MOVW LR, R11
73 p.As = AMOVW
74
75 p.From.Type = obj.TYPE_REG
76 p.From.Reg = REGLINK
77 p.To.Type = obj.TYPE_REG
78 p.To.Reg = REGTMP
79
80 // BL runtime.read_tls_fallback(SB)
81 p = obj.Appendp(p, newprog)
82
83 p.As = ABL
84 p.To.Type = obj.TYPE_BRANCH
85 p.To.Sym = progedit_tlsfallback
86 p.To.Offset = 0
87
88 // MOVW R11, LR
89 p = obj.Appendp(p, newprog)
90
91 p.As = AMOVW
92 p.From.Type = obj.TYPE_REG
93 p.From.Reg = REGTMP
94 p.To.Type = obj.TYPE_REG
95 p.To.Reg = REGLINK
96 break
97 }
98 }
99
100 // Otherwise, MRC/MCR instructions need no further treatment.
101 p.As = AWORD
102 }
103
104 // Rewrite float constants to values stored in memory.
105 switch p.As {
106 case AMOVF:
107 if 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) {
108 f32 := float32(p.From.Val.(float64))
109 p.From.Type = obj.TYPE_MEM
110 p.From.Sym = ctxt.Float32Sym(f32)
111 p.From.Name = obj.NAME_EXTERN
112 p.From.Offset = 0
113 }
114
115 case AMOVD:
116 if 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) {
117 p.From.Type = obj.TYPE_MEM
118 p.From.Sym = ctxt.Float64Sym(p.From.Val.(float64))
119 p.From.Name = obj.NAME_EXTERN
120 p.From.Offset = 0
121 }
122 }
123
124 if ctxt.Flag_dynlink {
125 c.rewriteToUseGot(p)
126 }
127 }
128
129 // Rewrite p, if necessary, to access global data via the global offset table.
130 func (c *ctxt5) rewriteToUseGot(p *obj.Prog) {
131 if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
132 // ADUFFxxx $offset
133 // becomes
134 // MOVW runtime.duffxxx@GOT, R9
135 // ADD $offset, R9
136 // CALL (R9)
137 var sym *obj.LSym
138 if p.As == obj.ADUFFZERO {
139 sym = c.ctxt.Lookup("runtime.duffzero")
140 } else {
141 sym = c.ctxt.Lookup("runtime.duffcopy")
142 }
143 offset := p.To.Offset
144 p.As = AMOVW
145 p.From.Type = obj.TYPE_MEM
146 p.From.Name = obj.NAME_GOTREF
147 p.From.Sym = sym
148 p.To.Type = obj.TYPE_REG
149 p.To.Reg = REG_R9
150 p.To.Name = obj.NAME_NONE
151 p.To.Offset = 0
152 p.To.Sym = nil
153 p1 := obj.Appendp(p, c.newprog)
154 p1.As = AADD
155 p1.From.Type = obj.TYPE_CONST
156 p1.From.Offset = offset
157 p1.To.Type = obj.TYPE_REG
158 p1.To.Reg = REG_R9
159 p2 := obj.Appendp(p1, c.newprog)
160 p2.As = obj.ACALL
161 p2.To.Type = obj.TYPE_MEM
162 p2.To.Reg = REG_R9
163 return
164 }
165
166 // We only care about global data: NAME_EXTERN means a global
167 // symbol in the Go sense, and p.Sym.Local is true for a few
168 // internally defined symbols.
169 if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
170 // MOVW $sym, Rx becomes MOVW sym@GOT, Rx
171 // MOVW $sym+<off>, Rx becomes MOVW sym@GOT, Rx; ADD <off>, Rx
172 if p.As != AMOVW {
173 c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
174 }
175 if p.To.Type != obj.TYPE_REG {
176 c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
177 }
178 p.From.Type = obj.TYPE_MEM
179 p.From.Name = obj.NAME_GOTREF
180 if p.From.Offset != 0 {
181 q := obj.Appendp(p, c.newprog)
182 q.As = AADD
183 q.From.Type = obj.TYPE_CONST
184 q.From.Offset = p.From.Offset
185 q.To = p.To
186 p.From.Offset = 0
187 }
188 }
189 if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
190 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
191 }
192 var source *obj.Addr
193 // MOVx sym, Ry becomes MOVW sym@GOT, R9; MOVx (R9), Ry
194 // 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.
196 if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
197 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
198 c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
199 }
200 source = &p.From
201 } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
202 source = &p.To
203 } else {
204 return
205 }
206 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
207 return
208 }
209 if source.Sym.Type == objabi.STLSBSS {
210 return
211 }
212 if source.Type != obj.TYPE_MEM {
213 c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
214 }
215 p1 := obj.Appendp(p, c.newprog)
216 p2 := obj.Appendp(p1, c.newprog)
217
218 p1.As = AMOVW
219 p1.From.Type = obj.TYPE_MEM
220 p1.From.Sym = source.Sym
221 p1.From.Name = obj.NAME_GOTREF
222 p1.To.Type = obj.TYPE_REG
223 p1.To.Reg = REG_R9
224
225 p2.As = p.As
226 p2.From = p.From
227 p2.To = p.To
228 if p.From.Name == obj.NAME_EXTERN {
229 p2.From.Reg = REG_R9
230 p2.From.Name = obj.NAME_NONE
231 p2.From.Sym = nil
232 } else if p.To.Name == obj.NAME_EXTERN {
233 p2.To.Reg = REG_R9
234 p2.To.Name = obj.NAME_NONE
235 p2.To.Sym = nil
236 } else {
237 return
238 }
239 obj.Nopout(p)
240 }
241
242 // Prog.mark
243 const (
244 FOLL = 1 << 0
245 LABEL = 1 << 1
246 LEAF = 1 << 2
247 )
248
249 func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
250 autosize := int32(0)
251
252 if cursym.Func.Text == nil || cursym.Func.Text.Link == nil {
253 return
254 }
255
256 c := ctxt5{ctxt: ctxt, cursym: cursym, newprog: newprog}
257
258 p := c.cursym.Func.Text
259 autoffset := int32(p.To.Offset)
260 if autoffset == -4 {
261 // Historical way to mark NOFRAME.
262 p.From.Sym.Set(obj.AttrNoFrame, true)
263 autoffset = 0
264 }
265 if autoffset < 0 || autoffset%4 != 0 {
266 c.ctxt.Diag("frame size %d not 0 or a positive multiple of 4", autoffset)
267 }
268 if p.From.Sym.NoFrame() {
269 if autoffset != 0 {
270 c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", autoffset)
271 }
272 }
273
274 cursym.Func.Locals = autoffset
275 cursym.Func.Args = p.To.Val.(int32)
276
277 /*
278 * find leaf subroutines
279 */
280 for p := cursym.Func.Text; p != nil; p = p.Link {
281 switch p.As {
282 case obj.ATEXT:
283 p.Mark |= LEAF
284
285 case ADIV, ADIVU, AMOD, AMODU:
286 cursym.Func.Text.Mark &^= LEAF
287
288 case ABL,
289 ABX,
290 obj.ADUFFZERO,
291 obj.ADUFFCOPY:
292 cursym.Func.Text.Mark &^= LEAF
293 }
294 }
295
296 var q2 *obj.Prog
297 for p := cursym.Func.Text; p != nil; p = p.Link {
298 o := p.As
299 switch o {
300 case obj.ATEXT:
301 autosize = autoffset
302
303 if p.Mark&LEAF != 0 && autosize == 0 {
304 // A leaf function with no locals has no frame.
305 p.From.Sym.Set(obj.AttrNoFrame, true)
306 }
307
308 if !p.From.Sym.NoFrame() {
309 // If there is a stack frame at all, it includes
310 // space to save the LR.
311 autosize += 4
312 }
313
314 if autosize == 0 && cursym.Func.Text.Mark&LEAF == 0 {
315 // A very few functions that do not return to their caller
316 // are not identified as leaves but still have no frame.
317 if ctxt.Debugvlog {
318 ctxt.Logf("save suppressed in: %s\n", cursym.Name)
319 }
320
321 cursym.Func.Text.Mark |= LEAF
322 }
323
324 // FP offsets need an updated p.To.Offset.
325 p.To.Offset = int64(autosize) - 4
326
327 if cursym.Func.Text.Mark&LEAF != 0 {
328 cursym.Set(obj.AttrLeaf, true)
329 if p.From.Sym.NoFrame() {
330 break
331 }
332 }
333
334 if !p.From.Sym.NoSplit() {
335 p = c.stacksplit(p, autosize) // emit split check
336 }
337
338 // MOVW.W R14,$-autosize(SP)
339 p = obj.Appendp(p, c.newprog)
340
341 p.As = AMOVW
342 p.Scond |= C_WBIT
343 p.From.Type = obj.TYPE_REG
344 p.From.Reg = REGLINK
345 p.To.Type = obj.TYPE_MEM
346 p.To.Offset = int64(-autosize)
347 p.To.Reg = REGSP
348 p.Spadj = autosize
349
350 if cursym.Func.Text.From.Sym.Wrapper() {
351 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
352 //
353 // MOVW g_panic(g), R1
354 // CMP $0, R1
355 // B.NE checkargp
356 // end:
357 // NOP
358 // ... function ...
359 // checkargp:
360 // MOVW panic_argp(R1), R2
361 // ADD $(autosize+4), R13, R3
362 // CMP R2, R3
363 // B.NE end
364 // ADD $4, R13, R4
365 // MOVW R4, panic_argp(R1)
366 // B end
367 //
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
371 p = obj.Appendp(p, newprog)
372 p.As = AMOVW
373 p.From.Type = obj.TYPE_MEM
374 p.From.Reg = REGG
375 p.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic
376 p.To.Type = obj.TYPE_REG
377 p.To.Reg = REG_R1
378
379 p = obj.Appendp(p, newprog)
380 p.As = ACMP
381 p.From.Type = obj.TYPE_CONST
382 p.From.Offset = 0
383 p.Reg = REG_R1
384
385 // B.NE checkargp
386 bne := obj.Appendp(p, newprog)
387 bne.As = ABNE
388 bne.To.Type = obj.TYPE_BRANCH
389
390 // end: NOP
391 end := obj.Appendp(bne, newprog)
392 end.As = obj.ANOP
393
394 // find end of function
395 var last *obj.Prog
396 for last = end; last.Link != nil; last = last.Link {
397 }
398
399 // MOVW panic_argp(R1), R2
400 mov := obj.Appendp(last, newprog)
401 mov.As = AMOVW
402 mov.From.Type = obj.TYPE_MEM
403 mov.From.Reg = REG_R1
404 mov.From.Offset = 0 // Panic.argp
405 mov.To.Type = obj.TYPE_REG
406 mov.To.Reg = REG_R2
407
408 // B.NE branch target is MOVW above
409 bne.To.SetTarget(mov)
410
411 // ADD $(autosize+4), R13, R3
412 p = obj.Appendp(mov, newprog)
413 p.As = AADD
414 p.From.Type = obj.TYPE_CONST
415 p.From.Offset = int64(autosize) + 4
416 p.Reg = REG_R13
417 p.To.Type = obj.TYPE_REG
418 p.To.Reg = REG_R3
419
420 // CMP R2, R3
421 p = obj.Appendp(p, newprog)
422 p.As = ACMP
423 p.From.Type = obj.TYPE_REG
424 p.From.Reg = REG_R2
425 p.Reg = REG_R3
426
427 // B.NE end
428 p = obj.Appendp(p, newprog)
429 p.As = ABNE
430 p.To.Type = obj.TYPE_BRANCH
431 p.To.SetTarget(end)
432
433 // ADD $4, R13, R4
434 p = obj.Appendp(p, newprog)
435 p.As = AADD
436 p.From.Type = obj.TYPE_CONST
437 p.From.Offset = 4
438 p.Reg = REG_R13
439 p.To.Type = obj.TYPE_REG
440 p.To.Reg = REG_R4
441
442 // MOVW R4, panic_argp(R1)
443 p = obj.Appendp(p, newprog)
444 p.As = AMOVW
445 p.From.Type = obj.TYPE_REG
446 p.From.Reg = REG_R4
447 p.To.Type = obj.TYPE_MEM
448 p.To.Reg = REG_R1
449 p.To.Offset = 0 // Panic.argp
450
451 // B end
452 p = obj.Appendp(p, newprog)
453 p.As = AB
454 p.To.Type = obj.TYPE_BRANCH
455 p.To.SetTarget(end)
456
457 // reset for subsequent passes
458 p = end
459 }
460
461 case obj.ARET:
462 nocache(p)
463 if cursym.Func.Text.Mark&LEAF != 0 {
464 if autosize == 0 {
465 p.As = AB
466 p.From = obj.Addr{}
467 if p.To.Sym != nil { // retjmp
468 p.To.Type = obj.TYPE_BRANCH
469 } else {
470 p.To.Type = obj.TYPE_MEM
471 p.To.Offset = 0
472 p.To.Reg = REGLINK
473 }
474
475 break
476 }
477 }
478
479 p.As = AMOVW
480 p.Scond |= C_PBIT
481 p.From.Type = obj.TYPE_MEM
482 p.From.Offset = int64(autosize)
483 p.From.Reg = REGSP
484 p.To.Type = obj.TYPE_REG
485 p.To.Reg = REGPC
486
487 // If there are instructions following
488 // this ARET, they come from a branch
489 // with the same stackframe, so no spadj.
490 if p.To.Sym != nil { // retjmp
491 p.To.Reg = REGLINK
492 q2 = obj.Appendp(p, newprog)
493 q2.As = AB
494 q2.To.Type = obj.TYPE_BRANCH
495 q2.To.Sym = p.To.Sym
496 p.To.Sym = nil
497 p = q2
498 }
499
500 case AADD:
501 if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
502 p.Spadj = int32(-p.From.Offset)
503 }
504
505 case ASUB:
506 if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
507 p.Spadj = int32(p.From.Offset)
508 }
509
510 case ADIV, ADIVU, AMOD, AMODU:
511 if cursym.Func.Text.From.Sym.NoSplit() {
512 ctxt.Diag("cannot divide in NOSPLIT function")
513 }
514 const debugdivmod = false
515 if debugdivmod {
516 break
517 }
518 if p.From.Type != obj.TYPE_REG {
519 break
520 }
521 if p.To.Type != obj.TYPE_REG {
522 break
523 }
524
525 // Make copy because we overwrite p below.
526 q1 := *p
527 if q1.Reg == REGTMP || q1.Reg == 0 && q1.To.Reg == REGTMP {
528 ctxt.Diag("div already using REGTMP: %v", p)
529 }
530
531 /* MOV m(g),REGTMP */
532 p.As = AMOVW
533 p.Pos = q1.Pos
534 p.From.Type = obj.TYPE_MEM
535 p.From.Reg = REGG
536 p.From.Offset = 6 * 4 // offset of g.m
537 p.Reg = 0
538 p.To.Type = obj.TYPE_REG
539 p.To.Reg = REGTMP
540
541 /* MOV a,m_divmod(REGTMP) */
542 p = obj.Appendp(p, newprog)
543 p.As = AMOVW
544 p.Pos = q1.Pos
545 p.From.Type = obj.TYPE_REG
546 p.From.Reg = q1.From.Reg
547 p.To.Type = obj.TYPE_MEM
548 p.To.Reg = REGTMP
549 p.To.Offset = 8 * 4 // offset of m.divmod
550
551 /* MOV b, R8 */
552 p = obj.Appendp(p, newprog)
553 p.As = AMOVW
554 p.Pos = q1.Pos
555 p.From.Type = obj.TYPE_REG
556 p.From.Reg = q1.Reg
557 if q1.Reg == 0 {
558 p.From.Reg = q1.To.Reg
559 }
560 p.To.Type = obj.TYPE_REG
561 p.To.Reg = REG_R8
562 p.To.Offset = 0
563
564 /* CALL appropriate */
565 p = obj.Appendp(p, newprog)
566 p.As = ABL
567 p.Pos = q1.Pos
568 p.To.Type = obj.TYPE_BRANCH
569 switch o {
570 case ADIV:
571 p.To.Sym = symdiv
572 case ADIVU:
573 p.To.Sym = symdivu
574 case AMOD:
575 p.To.Sym = symmod
576 case AMODU:
577 p.To.Sym = symmodu
578 }
579
580 /* MOV REGTMP, b */
581 p = obj.Appendp(p, newprog)
582 p.As = AMOVW
583 p.Pos = q1.Pos
584 p.From.Type = obj.TYPE_REG
585 p.From.Reg = REGTMP
586 p.From.Offset = 0
587 p.To.Type = obj.TYPE_REG
588 p.To.Reg = q1.To.Reg
589
590 case AMOVW:
591 if (p.Scond&C_WBIT != 0) && p.To.Type == obj.TYPE_MEM && p.To.Reg == REGSP {
592 p.Spadj = int32(-p.To.Offset)
593 }
594 if (p.Scond&C_PBIT != 0) && p.From.Type == obj.TYPE_MEM && p.From.Reg == REGSP && p.To.Reg != REGPC {
595 p.Spadj = int32(-p.From.Offset)
596 }
597 if p.From.Type == obj.TYPE_ADDR && p.From.Reg == REGSP && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
598 p.Spadj = int32(-p.From.Offset)
599 }
600
601 case obj.AGETCALLERPC:
602 if cursym.Leaf() {
603 /* MOVW LR, Rd */
604 p.As = AMOVW
605 p.From.Type = obj.TYPE_REG
606 p.From.Reg = REGLINK
607 } else {
608 /* MOVW (RSP), Rd */
609 p.As = AMOVW
610 p.From.Type = obj.TYPE_MEM
611 p.From.Reg = REGSP
612 }
613 }
614 }
615 }
616
617 func (c *ctxt5) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
618 // MOVW g_stackguard(g), R1
619 p = obj.Appendp(p, c.newprog)
620
621 p.As = AMOVW
622 p.From.Type = obj.TYPE_MEM
623 p.From.Reg = REGG
624 p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
625 if c.cursym.CFunc() {
626 p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
627 }
628 p.To.Type = obj.TYPE_REG
629 p.To.Reg = REG_R1
630
631 // Mark the stack bound check and morestack call async nonpreemptible.
632 // If we get preempted here, when resumed the preemption request is
633 // cleared, but we'll still call morestack, which will double the stack
634 // unnecessarily. See issue #35470.
635 p = c.ctxt.StartUnsafePoint(p, c.newprog)
636
637 if framesize <= objabi.StackSmall {
638 // small stack: SP < stackguard
639 // CMP stackguard, SP
640 p = obj.Appendp(p, c.newprog)
641
642 p.As = ACMP
643 p.From.Type = obj.TYPE_REG
644 p.From.Reg = REG_R1
645 p.Reg = REGSP
646 } else if framesize <= objabi.StackBig {
647 // large stack: SP-framesize < stackguard-StackSmall
648 // MOVW $-(framesize-StackSmall)(SP), R2
649 // CMP stackguard, R2
650 p = obj.Appendp(p, c.newprog)
651
652 p.As = AMOVW
653 p.From.Type = obj.TYPE_ADDR
654 p.From.Reg = REGSP
655 p.From.Offset = -(int64(framesize) - objabi.StackSmall)
656 p.To.Type = obj.TYPE_REG
657 p.To.Reg = REG_R2
658
659 p = obj.Appendp(p, c.newprog)
660 p.As = ACMP
661 p.From.Type = obj.TYPE_REG
662 p.From.Reg = REG_R1
663 p.Reg = REG_R2
664 } else {
665 // Such a large stack we need to protect against wraparound
666 // 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, R1
671 // MOVW.NE $StackGuard(SP), R2
672 // SUB.NE R1, R2
673 // MOVW.NE $(framesize+(StackGuard-StackSmall)), R3
674 // CMP.NE R3, R2
675 p = obj.Appendp(p, c.newprog)
676
677 p.As = ACMP
678 p.From.Type = obj.TYPE_CONST
679 p.From.Offset = int64(uint32(objabi.StackPreempt & (1<<32 - 1)))
680 p.Reg = REG_R1
681
682 p = obj.Appendp(p, c.newprog)
683 p.As = AMOVW
684 p.From.Type = obj.TYPE_ADDR
685 p.From.Reg = REGSP
686 p.From.Offset = int64(objabi.StackGuard)
687 p.To.Type = obj.TYPE_REG
688 p.To.Reg = REG_R2
689 p.Scond = C_SCOND_NE
690
691 p = obj.Appendp(p, c.newprog)
692 p.As = ASUB
693 p.From.Type = obj.TYPE_REG
694 p.From.Reg = REG_R1
695 p.To.Type = obj.TYPE_REG
696 p.To.Reg = REG_R2
697 p.Scond = C_SCOND_NE
698
699 p = obj.Appendp(p, c.newprog)
700 p.As = AMOVW
701 p.From.Type = obj.TYPE_ADDR
702 p.From.Offset = int64(framesize) + (int64(objabi.StackGuard) - objabi.StackSmall)
703 p.To.Type = obj.TYPE_REG
704 p.To.Reg = REG_R3
705 p.Scond = C_SCOND_NE
706
707 p = obj.Appendp(p, c.newprog)
708 p.As = ACMP
709 p.From.Type = obj.TYPE_REG
710 p.From.Reg = REG_R3
711 p.Reg = REG_R2
712 p.Scond = C_SCOND_NE
713 }
714
715 // BLS call-to-morestack
716 bls := obj.Appendp(p, c.newprog)
717 bls.As = ABLS
718 bls.To.Type = obj.TYPE_BRANCH
719
720 end := c.ctxt.EndUnsafePoint(bls, c.newprog, -1)
721
722 var last *obj.Prog
723 for last = c.cursym.Func.Text; last.Link != nil; last = last.Link {
724 }
725
726 // Now we are at the end of the function, but logically
727 // we are still in function prologue. We need to fix the
728 // SP data and PCDATA.
729 spfix := obj.Appendp(last, c.newprog)
730 spfix.As = obj.ANOP
731 spfix.Spadj = -framesize
732
733 pcdata := c.ctxt.EmitEntryStackMap(c.cursym, spfix, c.newprog)
734 pcdata = c.ctxt.StartUnsafePoint(pcdata, c.newprog)
735
736 // MOVW LR, R3
737 movw := obj.Appendp(pcdata, c.newprog)
738 movw.As = AMOVW
739 movw.From.Type = obj.TYPE_REG
740 movw.From.Reg = REGLINK
741 movw.To.Type = obj.TYPE_REG
742 movw.To.Reg = REG_R3
743
744 bls.To.SetTarget(movw)
745
746 // BL runtime.morestack
747 call := obj.Appendp(movw, c.newprog)
748 call.As = obj.ACALL
749 call.To.Type = obj.TYPE_BRANCH
750 morestack := "runtime.morestack"
751 switch {
752 case c.cursym.CFunc():
753 morestack = "runtime.morestackc"
754 case !c.cursym.Func.Text.From.Sym.NeedCtxt():
755 morestack = "runtime.morestack_noctxt"
756 }
757 call.To.Sym = c.ctxt.Lookup(morestack)
758
759 pcdata = c.ctxt.EndUnsafePoint(call, c.newprog, -1)
760
761 // B start
762 b := obj.Appendp(pcdata, c.newprog)
763 b.As = obj.AJMP
764 b.To.Type = obj.TYPE_BRANCH
765 b.To.SetTarget(c.cursym.Func.Text.Link)
766 b.Spadj = +framesize
767
768 return end
769 }
770
771 var unaryDst = map[obj.As]bool{
772 ASWI: true,
773 AWORD: true,
774 }
775
776 var Linkarm = obj.LinkArch{
777 Arch: sys.ArchARM,
778 Init: buildop,
779 Preprocess: preprocess,
780 Assemble: span5,
781 Progedit: progedit,
782 UnaryDst: unaryDst,
783 DWARFRegisters: ARMDWARFRegisters,
784 }
785