blob: 6e5390c24ec77d73385dbd9dc08aed3a6c668b51 [file] [log] [blame]
Dan Willemsen09eb3b12015-09-16 14:34:17 -07001// Derived from Inferno utils/5c/swt.c
Dan Willemsenebae3022017-01-13 23:01:08 -08002// https://bitbucket.org/inferno-os/inferno-os/src/default/utils/5c/swt.c
Dan Willemsen09eb3b12015-09-16 14:34:17 -07003//
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
Dan Willemsen38f2dba2016-07-08 14:54:35 -070011// Portions Copyright © 2009 The Go Authors. All rights reserved.
Dan Willemsen09eb3b12015-09-16 14:34:17 -070012//
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 arm
32
33import (
34 "cmd/internal/obj"
Dan Willemsen38f2dba2016-07-08 14:54:35 -070035 "cmd/internal/sys"
Dan Willemsen09eb3b12015-09-16 14:34:17 -070036 "fmt"
37 "log"
38 "math"
39)
40
41var progedit_tlsfallback *obj.LSym
42
43func progedit(ctxt *obj.Link, p *obj.Prog) {
44 p.From.Class = 0
45 p.To.Class = 0
46
47 // Rewrite B/BL to symbol as TYPE_BRANCH.
48 switch p.As {
49 case AB,
50 ABL,
51 obj.ADUFFZERO,
52 obj.ADUFFCOPY:
53 if p.To.Type == obj.TYPE_MEM && (p.To.Name == obj.NAME_EXTERN || p.To.Name == obj.NAME_STATIC) && p.To.Sym != nil {
54 p.To.Type = obj.TYPE_BRANCH
55 }
56 }
57
Dan Willemsen38f2dba2016-07-08 14:54:35 -070058 // Replace TLS register fetches on older ARM processors.
Dan Willemsen09eb3b12015-09-16 14:34:17 -070059 switch p.As {
60 // Treat MRC 15, 0, <reg>, C13, C0, 3 specially.
61 case AMRC:
62 if p.To.Offset&0xffff0fff == 0xee1d0f70 {
Dan Willemsen38f2dba2016-07-08 14:54:35 -070063 // Because the instruction might be rewritten to a BL which returns in R0
Dan Willemsen09eb3b12015-09-16 14:34:17 -070064 // the register must be zero.
65 if p.To.Offset&0xf000 != 0 {
66 ctxt.Diag("%v: TLS MRC instruction must write to R0 as it might get translated into a BL instruction", p.Line())
67 }
68
Dan Willemsenebae3022017-01-13 23:01:08 -080069 if obj.GOARM < 7 {
Dan Willemsen09eb3b12015-09-16 14:34:17 -070070 // Replace it with BL runtime.read_tls_fallback(SB) for ARM CPUs that lack the tls extension.
71 if progedit_tlsfallback == nil {
72 progedit_tlsfallback = obj.Linklookup(ctxt, "runtime.read_tls_fallback", 0)
73 }
74
75 // MOVW LR, R11
76 p.As = AMOVW
77
78 p.From.Type = obj.TYPE_REG
79 p.From.Reg = REGLINK
80 p.To.Type = obj.TYPE_REG
81 p.To.Reg = REGTMP
82
83 // BL runtime.read_tls_fallback(SB)
84 p = obj.Appendp(ctxt, p)
85
86 p.As = ABL
87 p.To.Type = obj.TYPE_BRANCH
88 p.To.Sym = progedit_tlsfallback
89 p.To.Offset = 0
90
91 // MOVW R11, LR
92 p = obj.Appendp(ctxt, p)
93
94 p.As = AMOVW
95 p.From.Type = obj.TYPE_REG
96 p.From.Reg = REGTMP
97 p.To.Type = obj.TYPE_REG
98 p.To.Reg = REGLINK
99 break
100 }
101 }
102
103 // Otherwise, MRC/MCR instructions need no further treatment.
104 p.As = AWORD
105 }
106
107 // Rewrite float constants to values stored in memory.
108 switch p.As {
109 case AMOVF:
110 if p.From.Type == obj.TYPE_FCONST && chipfloat5(ctxt, p.From.Val.(float64)) < 0 && (chipzero5(ctxt, p.From.Val.(float64)) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) {
111 f32 := float32(p.From.Val.(float64))
112 i32 := math.Float32bits(f32)
113 literal := fmt.Sprintf("$f32.%08x", i32)
114 s := obj.Linklookup(ctxt, literal, 0)
115 p.From.Type = obj.TYPE_MEM
116 p.From.Sym = s
117 p.From.Name = obj.NAME_EXTERN
118 p.From.Offset = 0
119 }
120
121 case AMOVD:
122 if p.From.Type == obj.TYPE_FCONST && chipfloat5(ctxt, p.From.Val.(float64)) < 0 && (chipzero5(ctxt, p.From.Val.(float64)) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) {
123 i64 := math.Float64bits(p.From.Val.(float64))
124 literal := fmt.Sprintf("$f64.%016x", i64)
125 s := obj.Linklookup(ctxt, literal, 0)
126 p.From.Type = obj.TYPE_MEM
127 p.From.Sym = s
128 p.From.Name = obj.NAME_EXTERN
129 p.From.Offset = 0
130 }
131 }
132
Dan Willemsen38f2dba2016-07-08 14:54:35 -0700133 if ctxt.Flag_dynlink {
134 rewriteToUseGot(ctxt, p)
135 }
136}
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700137
Dan Willemsen38f2dba2016-07-08 14:54:35 -0700138// Rewrite p, if necessary, to access global data via the global offset table.
139func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) {
140 if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
141 // ADUFFxxx $offset
142 // becomes
143 // MOVW runtime.duffxxx@GOT, R9
144 // ADD $offset, R9
145 // CALL (R9)
146 var sym *obj.LSym
147 if p.As == obj.ADUFFZERO {
148 sym = obj.Linklookup(ctxt, "runtime.duffzero", 0)
149 } else {
150 sym = obj.Linklookup(ctxt, "runtime.duffcopy", 0)
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700151 }
Dan Willemsen38f2dba2016-07-08 14:54:35 -0700152 offset := p.To.Offset
153 p.As = AMOVW
154 p.From.Type = obj.TYPE_MEM
155 p.From.Name = obj.NAME_GOTREF
156 p.From.Sym = sym
157 p.To.Type = obj.TYPE_REG
158 p.To.Reg = REG_R9
159 p.To.Name = obj.NAME_NONE
160 p.To.Offset = 0
161 p.To.Sym = nil
162 p1 := obj.Appendp(ctxt, p)
163 p1.As = AADD
164 p1.From.Type = obj.TYPE_CONST
165 p1.From.Offset = offset
166 p1.To.Type = obj.TYPE_REG
167 p1.To.Reg = REG_R9
168 p2 := obj.Appendp(ctxt, p1)
169 p2.As = obj.ACALL
170 p2.To.Type = obj.TYPE_MEM
171 p2.To.Reg = REG_R9
172 return
173 }
174
175 // We only care about global data: NAME_EXTERN means a global
176 // symbol in the Go sense, and p.Sym.Local is true for a few
177 // internally defined symbols.
Dan Willemsenebae3022017-01-13 23:01:08 -0800178 if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
Dan Willemsen38f2dba2016-07-08 14:54:35 -0700179 // MOVW $sym, Rx becomes MOVW sym@GOT, Rx
180 // MOVW $sym+<off>, Rx becomes MOVW sym@GOT, Rx; ADD <off>, Rx
181 if p.As != AMOVW {
182 ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
183 }
184 if p.To.Type != obj.TYPE_REG {
185 ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
186 }
187 p.From.Type = obj.TYPE_MEM
188 p.From.Name = obj.NAME_GOTREF
189 if p.From.Offset != 0 {
190 q := obj.Appendp(ctxt, p)
191 q.As = AADD
192 q.From.Type = obj.TYPE_CONST
193 q.From.Offset = p.From.Offset
194 q.To = p.To
195 p.From.Offset = 0
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700196 }
197 }
Dan Willemsen38f2dba2016-07-08 14:54:35 -0700198 if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN {
199 ctxt.Diag("don't know how to handle %v with -dynlink", p)
200 }
201 var source *obj.Addr
202 // MOVx sym, Ry becomes MOVW sym@GOT, R9; MOVx (R9), Ry
203 // MOVx Ry, sym becomes MOVW sym@GOT, R9; MOVx Ry, (R9)
204 // An addition may be inserted between the two MOVs if there is an offset.
Dan Willemsenebae3022017-01-13 23:01:08 -0800205 if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
206 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
Dan Willemsen38f2dba2016-07-08 14:54:35 -0700207 ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
208 }
209 source = &p.From
Dan Willemsenebae3022017-01-13 23:01:08 -0800210 } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
Dan Willemsen38f2dba2016-07-08 14:54:35 -0700211 source = &p.To
212 } else {
213 return
214 }
215 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
216 return
217 }
218 if source.Sym.Type == obj.STLSBSS {
219 return
220 }
221 if source.Type != obj.TYPE_MEM {
222 ctxt.Diag("don't know how to handle %v with -dynlink", p)
223 }
224 p1 := obj.Appendp(ctxt, p)
225 p2 := obj.Appendp(ctxt, p1)
226
227 p1.As = AMOVW
228 p1.From.Type = obj.TYPE_MEM
229 p1.From.Sym = source.Sym
230 p1.From.Name = obj.NAME_GOTREF
231 p1.To.Type = obj.TYPE_REG
232 p1.To.Reg = REG_R9
233
234 p2.As = p.As
235 p2.From = p.From
236 p2.To = p.To
237 if p.From.Name == obj.NAME_EXTERN {
238 p2.From.Reg = REG_R9
239 p2.From.Name = obj.NAME_NONE
240 p2.From.Sym = nil
241 } else if p.To.Name == obj.NAME_EXTERN {
242 p2.To.Reg = REG_R9
243 p2.To.Name = obj.NAME_NONE
244 p2.To.Sym = nil
245 } else {
246 return
247 }
248 obj.Nopout(p)
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700249}
250
251// Prog.mark
252const (
253 FOLL = 1 << 0
254 LABEL = 1 << 1
255 LEAF = 1 << 2
256)
257
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700258func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
259 autosize := int32(0)
260
261 ctxt.Cursym = cursym
262
263 if cursym.Text == nil || cursym.Text.Link == nil {
264 return
265 }
266
267 softfloat(ctxt, cursym)
268
269 p := cursym.Text
270 autoffset := int32(p.To.Offset)
271 if autoffset < 0 {
272 autoffset = 0
273 }
274 cursym.Locals = autoffset
275 cursym.Args = p.To.Val.(int32)
276
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700277 /*
278 * find leaf subroutines
279 * strip NOPs
280 * expand RET
281 * expand BECOME pseudo
282 */
283 var q1 *obj.Prog
284 var q *obj.Prog
285 for p := cursym.Text; p != nil; p = p.Link {
286 switch p.As {
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700287 case obj.ATEXT:
288 p.Mark |= LEAF
289
290 case obj.ARET:
291 break
292
293 case ADIV, ADIVU, AMOD, AMODU:
294 q = p
295 if ctxt.Sym_div == nil {
296 initdiv(ctxt)
297 }
298 cursym.Text.Mark &^= LEAF
299 continue
300
301 case obj.ANOP:
302 q1 = p.Link
303 q.Link = q1 /* q is non-nop */
304 if q1 != nil {
305 q1.Mark |= p.Mark
306 }
307 continue
308
309 case ABL,
310 ABX,
311 obj.ADUFFZERO,
312 obj.ADUFFCOPY:
313 cursym.Text.Mark &^= LEAF
314 fallthrough
315
Dan Willemsen38f2dba2016-07-08 14:54:35 -0700316 case AB,
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700317 ABEQ,
318 ABNE,
319 ABCS,
320 ABHS,
321 ABCC,
322 ABLO,
323 ABMI,
324 ABPL,
325 ABVS,
326 ABVC,
327 ABHI,
328 ABLS,
329 ABGE,
330 ABLT,
331 ABGT,
332 ABLE:
333 q1 = p.Pcond
334 if q1 != nil {
335 for q1.As == obj.ANOP {
336 q1 = q1.Link
337 p.Pcond = q1
338 }
339 }
340 }
341
342 q = p
343 }
344
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700345 var p1 *obj.Prog
346 var p2 *obj.Prog
347 var q2 *obj.Prog
348 for p := cursym.Text; p != nil; p = p.Link {
Dan Willemsen38f2dba2016-07-08 14:54:35 -0700349 o := p.As
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700350 switch o {
351 case obj.ATEXT:
352 autosize = int32(p.To.Offset + 4)
353 if autosize <= 4 {
354 if cursym.Text.Mark&LEAF != 0 {
355 p.To.Offset = -4
356 autosize = 0
357 }
358 }
359
360 if autosize == 0 && cursym.Text.Mark&LEAF == 0 {
361 if ctxt.Debugvlog != 0 {
Dan Willemsenebae3022017-01-13 23:01:08 -0800362 ctxt.Logf("save suppressed in: %s\n", cursym.Name)
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700363 }
364
365 cursym.Text.Mark |= LEAF
366 }
367
368 if cursym.Text.Mark&LEAF != 0 {
Dan Willemsenebae3022017-01-13 23:01:08 -0800369 cursym.Set(obj.AttrLeaf, true)
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700370 if autosize == 0 {
371 break
372 }
373 }
374
375 if p.From3.Offset&obj.NOSPLIT == 0 {
376 p = stacksplit(ctxt, p, autosize) // emit split check
377 }
378
379 // MOVW.W R14,$-autosize(SP)
380 p = obj.Appendp(ctxt, p)
381
382 p.As = AMOVW
383 p.Scond |= C_WBIT
384 p.From.Type = obj.TYPE_REG
385 p.From.Reg = REGLINK
386 p.To.Type = obj.TYPE_MEM
387 p.To.Offset = int64(-autosize)
388 p.To.Reg = REGSP
389 p.Spadj = autosize
390
391 if cursym.Text.From3.Offset&obj.WRAPPER != 0 {
392 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
393 //
394 // MOVW g_panic(g), R1
395 // CMP $0, R1
396 // B.EQ end
397 // MOVW panic_argp(R1), R2
398 // ADD $(autosize+4), R13, R3
399 // CMP R2, R3
400 // B.NE end
401 // ADD $4, R13, R4
402 // MOVW R4, panic_argp(R1)
403 // end:
404 // NOP
405 //
406 // The NOP is needed to give the jumps somewhere to land.
407 // It is a liblink NOP, not an ARM NOP: it encodes to 0 instruction bytes.
408
409 p = obj.Appendp(ctxt, p)
410
411 p.As = AMOVW
412 p.From.Type = obj.TYPE_MEM
413 p.From.Reg = REGG
Dan Willemsen38f2dba2016-07-08 14:54:35 -0700414 p.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700415 p.To.Type = obj.TYPE_REG
416 p.To.Reg = REG_R1
417
418 p = obj.Appendp(ctxt, p)
419 p.As = ACMP
420 p.From.Type = obj.TYPE_CONST
421 p.From.Offset = 0
422 p.Reg = REG_R1
423
424 p = obj.Appendp(ctxt, p)
425 p.As = ABEQ
426 p.To.Type = obj.TYPE_BRANCH
427 p1 = p
428
429 p = obj.Appendp(ctxt, p)
430 p.As = AMOVW
431 p.From.Type = obj.TYPE_MEM
432 p.From.Reg = REG_R1
433 p.From.Offset = 0 // Panic.argp
434 p.To.Type = obj.TYPE_REG
435 p.To.Reg = REG_R2
436
437 p = obj.Appendp(ctxt, p)
438 p.As = AADD
439 p.From.Type = obj.TYPE_CONST
440 p.From.Offset = int64(autosize) + 4
441 p.Reg = REG_R13
442 p.To.Type = obj.TYPE_REG
443 p.To.Reg = REG_R3
444
445 p = obj.Appendp(ctxt, p)
446 p.As = ACMP
447 p.From.Type = obj.TYPE_REG
448 p.From.Reg = REG_R2
449 p.Reg = REG_R3
450
451 p = obj.Appendp(ctxt, p)
452 p.As = ABNE
453 p.To.Type = obj.TYPE_BRANCH
454 p2 = p
455
456 p = obj.Appendp(ctxt, p)
457 p.As = AADD
458 p.From.Type = obj.TYPE_CONST
459 p.From.Offset = 4
460 p.Reg = REG_R13
461 p.To.Type = obj.TYPE_REG
462 p.To.Reg = REG_R4
463
464 p = obj.Appendp(ctxt, p)
465 p.As = AMOVW
466 p.From.Type = obj.TYPE_REG
467 p.From.Reg = REG_R4
468 p.To.Type = obj.TYPE_MEM
469 p.To.Reg = REG_R1
470 p.To.Offset = 0 // Panic.argp
471
472 p = obj.Appendp(ctxt, p)
473
474 p.As = obj.ANOP
475 p1.Pcond = p
476 p2.Pcond = p
477 }
478
479 case obj.ARET:
Dan Willemsen38f2dba2016-07-08 14:54:35 -0700480 nocache(p)
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700481 if cursym.Text.Mark&LEAF != 0 {
482 if autosize == 0 {
483 p.As = AB
484 p.From = obj.Addr{}
485 if p.To.Sym != nil { // retjmp
486 p.To.Type = obj.TYPE_BRANCH
487 } else {
488 p.To.Type = obj.TYPE_MEM
489 p.To.Offset = 0
490 p.To.Reg = REGLINK
491 }
492
493 break
494 }
495 }
496
497 p.As = AMOVW
498 p.Scond |= C_PBIT
499 p.From.Type = obj.TYPE_MEM
500 p.From.Offset = int64(autosize)
501 p.From.Reg = REGSP
502 p.To.Type = obj.TYPE_REG
503 p.To.Reg = REGPC
504
505 // If there are instructions following
506 // this ARET, they come from a branch
507 // with the same stackframe, so no spadj.
508 if p.To.Sym != nil { // retjmp
509 p.To.Reg = REGLINK
510 q2 = obj.Appendp(ctxt, p)
511 q2.As = AB
512 q2.To.Type = obj.TYPE_BRANCH
513 q2.To.Sym = p.To.Sym
514 p.To.Sym = nil
515 p = q2
516 }
517
518 case AADD:
519 if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
520 p.Spadj = int32(-p.From.Offset)
521 }
522
523 case ASUB:
524 if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
525 p.Spadj = int32(p.From.Offset)
526 }
527
528 case ADIV, ADIVU, AMOD, AMODU:
529 if cursym.Text.From3.Offset&obj.NOSPLIT != 0 {
530 ctxt.Diag("cannot divide in NOSPLIT function")
531 }
532 if ctxt.Debugdivmod != 0 {
533 break
534 }
535 if p.From.Type != obj.TYPE_REG {
536 break
537 }
538 if p.To.Type != obj.TYPE_REG {
539 break
540 }
541
542 // Make copy because we overwrite p below.
543 q1 := *p
544 if q1.Reg == REGTMP || q1.Reg == 0 && q1.To.Reg == REGTMP {
545 ctxt.Diag("div already using REGTMP: %v", p)
546 }
547
548 /* MOV m(g),REGTMP */
549 p.As = AMOVW
550 p.Lineno = q1.Lineno
551 p.From.Type = obj.TYPE_MEM
552 p.From.Reg = REGG
553 p.From.Offset = 6 * 4 // offset of g.m
554 p.Reg = 0
555 p.To.Type = obj.TYPE_REG
556 p.To.Reg = REGTMP
557
558 /* MOV a,m_divmod(REGTMP) */
559 p = obj.Appendp(ctxt, p)
560 p.As = AMOVW
561 p.Lineno = q1.Lineno
562 p.From.Type = obj.TYPE_REG
563 p.From.Reg = q1.From.Reg
564 p.To.Type = obj.TYPE_MEM
565 p.To.Reg = REGTMP
566 p.To.Offset = 8 * 4 // offset of m.divmod
567
Dan Willemsenebae3022017-01-13 23:01:08 -0800568 /* MOV b, R8 */
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700569 p = obj.Appendp(ctxt, p)
570 p.As = AMOVW
571 p.Lineno = q1.Lineno
572 p.From.Type = obj.TYPE_REG
573 p.From.Reg = q1.Reg
574 if q1.Reg == 0 {
575 p.From.Reg = q1.To.Reg
576 }
577 p.To.Type = obj.TYPE_REG
Dan Willemsenebae3022017-01-13 23:01:08 -0800578 p.To.Reg = REG_R8
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700579 p.To.Offset = 0
580
581 /* CALL appropriate */
582 p = obj.Appendp(ctxt, p)
583 p.As = ABL
584 p.Lineno = q1.Lineno
585 p.To.Type = obj.TYPE_BRANCH
586 switch o {
587 case ADIV:
588 p.To.Sym = ctxt.Sym_div
589
590 case ADIVU:
591 p.To.Sym = ctxt.Sym_divu
592
593 case AMOD:
594 p.To.Sym = ctxt.Sym_mod
595
596 case AMODU:
597 p.To.Sym = ctxt.Sym_modu
598 }
599
600 /* MOV REGTMP, b */
601 p = obj.Appendp(ctxt, p)
602 p.As = AMOVW
603 p.Lineno = q1.Lineno
604 p.From.Type = obj.TYPE_REG
605 p.From.Reg = REGTMP
606 p.From.Offset = 0
607 p.To.Type = obj.TYPE_REG
608 p.To.Reg = q1.To.Reg
609
610 case AMOVW:
611 if (p.Scond&C_WBIT != 0) && p.To.Type == obj.TYPE_MEM && p.To.Reg == REGSP {
612 p.Spadj = int32(-p.To.Offset)
613 }
614 if (p.Scond&C_PBIT != 0) && p.From.Type == obj.TYPE_MEM && p.From.Reg == REGSP && p.To.Reg != REGPC {
615 p.Spadj = int32(-p.From.Offset)
616 }
617 if p.From.Type == obj.TYPE_ADDR && p.From.Reg == REGSP && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
618 p.Spadj = int32(-p.From.Offset)
619 }
620 }
621 }
622}
623
624func isfloatreg(a *obj.Addr) bool {
625 return a.Type == obj.TYPE_REG && REG_F0 <= a.Reg && a.Reg <= REG_F15
626}
627
628func softfloat(ctxt *obj.Link, cursym *obj.LSym) {
Dan Willemsenebae3022017-01-13 23:01:08 -0800629 if obj.GOARM > 5 {
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700630 return
631 }
632
633 symsfloat := obj.Linklookup(ctxt, "_sfloat", 0)
634
635 wasfloat := 0
636 for p := cursym.Text; p != nil; p = p.Link {
637 if p.Pcond != nil {
638 p.Pcond.Mark |= LABEL
639 }
640 }
641 var next *obj.Prog
642 for p := cursym.Text; p != nil; p = p.Link {
643 switch p.As {
644 case AMOVW:
645 if isfloatreg(&p.To) || isfloatreg(&p.From) {
646 goto soft
647 }
648 goto notsoft
649
650 case AMOVWD,
651 AMOVWF,
652 AMOVDW,
653 AMOVFW,
654 AMOVFD,
655 AMOVDF,
656 AMOVF,
657 AMOVD,
658 ACMPF,
659 ACMPD,
660 AADDF,
661 AADDD,
662 ASUBF,
663 ASUBD,
664 AMULF,
665 AMULD,
666 ADIVF,
667 ADIVD,
668 ASQRTF,
669 ASQRTD,
670 AABSF,
Dan Willemsenebae3022017-01-13 23:01:08 -0800671 AABSD,
672 ANEGF,
673 ANEGD:
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700674 goto soft
675
676 default:
677 goto notsoft
678 }
679
680 soft:
681 if wasfloat == 0 || (p.Mark&LABEL != 0) {
682 next = ctxt.NewProg()
683 *next = *p
684
685 // BL _sfloat(SB)
686 *p = obj.Prog{}
687 p.Ctxt = ctxt
688 p.Link = next
689 p.As = ABL
690 p.To.Type = obj.TYPE_BRANCH
691 p.To.Sym = symsfloat
692 p.Lineno = next.Lineno
693
694 p = next
695 wasfloat = 1
696 }
697
698 continue
699
700 notsoft:
701 wasfloat = 0
702 }
703}
704
705func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog {
706 // MOVW g_stackguard(g), R1
707 p = obj.Appendp(ctxt, p)
708
709 p.As = AMOVW
710 p.From.Type = obj.TYPE_MEM
711 p.From.Reg = REGG
Dan Willemsen38f2dba2016-07-08 14:54:35 -0700712 p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
Dan Willemsenebae3022017-01-13 23:01:08 -0800713 if ctxt.Cursym.CFunc() {
Dan Willemsen38f2dba2016-07-08 14:54:35 -0700714 p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700715 }
716 p.To.Type = obj.TYPE_REG
717 p.To.Reg = REG_R1
718
719 if framesize <= obj.StackSmall {
720 // small stack: SP < stackguard
721 // CMP stackguard, SP
722 p = obj.Appendp(ctxt, p)
723
724 p.As = ACMP
725 p.From.Type = obj.TYPE_REG
726 p.From.Reg = REG_R1
727 p.Reg = REGSP
728 } else if framesize <= obj.StackBig {
729 // large stack: SP-framesize < stackguard-StackSmall
730 // MOVW $-framesize(SP), R2
731 // CMP stackguard, R2
732 p = obj.Appendp(ctxt, p)
733
734 p.As = AMOVW
735 p.From.Type = obj.TYPE_ADDR
736 p.From.Reg = REGSP
737 p.From.Offset = int64(-framesize)
738 p.To.Type = obj.TYPE_REG
739 p.To.Reg = REG_R2
740
741 p = obj.Appendp(ctxt, p)
742 p.As = ACMP
743 p.From.Type = obj.TYPE_REG
744 p.From.Reg = REG_R1
745 p.Reg = REG_R2
746 } else {
747 // Such a large stack we need to protect against wraparound
748 // if SP is close to zero.
749 // SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall)
750 // The +StackGuard on both sides is required to keep the left side positive:
751 // SP is allowed to be slightly below stackguard. See stack.h.
752 // CMP $StackPreempt, R1
753 // MOVW.NE $StackGuard(SP), R2
754 // SUB.NE R1, R2
755 // MOVW.NE $(framesize+(StackGuard-StackSmall)), R3
756 // CMP.NE R3, R2
757 p = obj.Appendp(ctxt, p)
758
759 p.As = ACMP
760 p.From.Type = obj.TYPE_CONST
761 p.From.Offset = int64(uint32(obj.StackPreempt & (1<<32 - 1)))
762 p.Reg = REG_R1
763
764 p = obj.Appendp(ctxt, p)
765 p.As = AMOVW
766 p.From.Type = obj.TYPE_ADDR
767 p.From.Reg = REGSP
768 p.From.Offset = obj.StackGuard
769 p.To.Type = obj.TYPE_REG
770 p.To.Reg = REG_R2
771 p.Scond = C_SCOND_NE
772
773 p = obj.Appendp(ctxt, p)
774 p.As = ASUB
775 p.From.Type = obj.TYPE_REG
776 p.From.Reg = REG_R1
777 p.To.Type = obj.TYPE_REG
778 p.To.Reg = REG_R2
779 p.Scond = C_SCOND_NE
780
781 p = obj.Appendp(ctxt, p)
782 p.As = AMOVW
783 p.From.Type = obj.TYPE_ADDR
784 p.From.Offset = int64(framesize) + (obj.StackGuard - obj.StackSmall)
785 p.To.Type = obj.TYPE_REG
786 p.To.Reg = REG_R3
787 p.Scond = C_SCOND_NE
788
789 p = obj.Appendp(ctxt, p)
790 p.As = ACMP
791 p.From.Type = obj.TYPE_REG
792 p.From.Reg = REG_R3
793 p.Reg = REG_R2
794 p.Scond = C_SCOND_NE
795 }
796
797 // BLS call-to-morestack
798 bls := obj.Appendp(ctxt, p)
799 bls.As = ABLS
800 bls.To.Type = obj.TYPE_BRANCH
801
802 var last *obj.Prog
803 for last = ctxt.Cursym.Text; last.Link != nil; last = last.Link {
804 }
805
Dan Willemsenebae3022017-01-13 23:01:08 -0800806 // Now we are at the end of the function, but logically
807 // we are still in function prologue. We need to fix the
808 // SP data and PCDATA.
Dan Willemsen38f2dba2016-07-08 14:54:35 -0700809 spfix := obj.Appendp(ctxt, last)
810 spfix.As = obj.ANOP
811 spfix.Spadj = -framesize
812
Dan Willemsenebae3022017-01-13 23:01:08 -0800813 pcdata := obj.Appendp(ctxt, spfix)
814 pcdata.Lineno = ctxt.Cursym.Text.Lineno
815 pcdata.Mode = ctxt.Cursym.Text.Mode
816 pcdata.As = obj.APCDATA
817 pcdata.From.Type = obj.TYPE_CONST
818 pcdata.From.Offset = obj.PCDATA_StackMapIndex
819 pcdata.To.Type = obj.TYPE_CONST
820 pcdata.To.Offset = -1 // pcdata starts at -1 at function entry
821
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700822 // MOVW LR, R3
Dan Willemsenebae3022017-01-13 23:01:08 -0800823 movw := obj.Appendp(ctxt, pcdata)
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700824 movw.As = AMOVW
825 movw.From.Type = obj.TYPE_REG
826 movw.From.Reg = REGLINK
827 movw.To.Type = obj.TYPE_REG
828 movw.To.Reg = REG_R3
829
830 bls.Pcond = movw
831
832 // BL runtime.morestack
833 call := obj.Appendp(ctxt, movw)
834 call.As = obj.ACALL
835 call.To.Type = obj.TYPE_BRANCH
836 morestack := "runtime.morestack"
837 switch {
Dan Willemsenebae3022017-01-13 23:01:08 -0800838 case ctxt.Cursym.CFunc():
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700839 morestack = "runtime.morestackc"
840 case ctxt.Cursym.Text.From3.Offset&obj.NEEDCTXT == 0:
841 morestack = "runtime.morestack_noctxt"
842 }
843 call.To.Sym = obj.Linklookup(ctxt, morestack, 0)
844
845 // B start
846 b := obj.Appendp(ctxt, call)
847 b.As = obj.AJMP
848 b.To.Type = obj.TYPE_BRANCH
849 b.Pcond = ctxt.Cursym.Text.Link
Dan Willemsen38f2dba2016-07-08 14:54:35 -0700850 b.Spadj = +framesize
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700851
852 return bls
853}
854
855func initdiv(ctxt *obj.Link) {
856 if ctxt.Sym_div != nil {
857 return
858 }
859 ctxt.Sym_div = obj.Linklookup(ctxt, "_div", 0)
860 ctxt.Sym_divu = obj.Linklookup(ctxt, "_divu", 0)
861 ctxt.Sym_mod = obj.Linklookup(ctxt, "_mod", 0)
862 ctxt.Sym_modu = obj.Linklookup(ctxt, "_modu", 0)
863}
864
865func follow(ctxt *obj.Link, s *obj.LSym) {
866 ctxt.Cursym = s
867
868 firstp := ctxt.NewProg()
869 lastp := firstp
870 xfol(ctxt, s.Text, &lastp)
871 lastp.Link = nil
872 s.Text = firstp.Link
873}
874
Dan Willemsen38f2dba2016-07-08 14:54:35 -0700875func relinv(a obj.As) obj.As {
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700876 switch a {
877 case ABEQ:
878 return ABNE
879 case ABNE:
880 return ABEQ
881 case ABCS:
882 return ABCC
883 case ABHS:
884 return ABLO
885 case ABCC:
886 return ABCS
887 case ABLO:
888 return ABHS
889 case ABMI:
890 return ABPL
891 case ABPL:
892 return ABMI
893 case ABVS:
894 return ABVC
895 case ABVC:
896 return ABVS
897 case ABHI:
898 return ABLS
899 case ABLS:
900 return ABHI
901 case ABGE:
902 return ABLT
903 case ABLT:
904 return ABGE
905 case ABGT:
906 return ABLE
907 case ABLE:
908 return ABGT
909 }
910
911 log.Fatalf("unknown relation: %s", Anames[a])
912 return 0
913}
914
915func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) {
916 var q *obj.Prog
917 var r *obj.Prog
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700918 var i int
919
920loop:
921 if p == nil {
922 return
923 }
Dan Willemsen38f2dba2016-07-08 14:54:35 -0700924 a := p.As
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700925 if a == AB {
926 q = p.Pcond
927 if q != nil && q.As != obj.ATEXT {
928 p.Mark |= FOLL
929 p = q
930 if p.Mark&FOLL == 0 {
931 goto loop
932 }
933 }
934 }
935
936 if p.Mark&FOLL != 0 {
937 i = 0
938 q = p
939 for ; i < 4; i, q = i+1, q.Link {
940 if q == *last || q == nil {
941 break
942 }
Dan Willemsen38f2dba2016-07-08 14:54:35 -0700943 a = q.As
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700944 if a == obj.ANOP {
945 i--
946 continue
947 }
948
949 if a == AB || (a == obj.ARET && q.Scond == C_SCOND_NONE) || a == ARFE || a == obj.AUNDEF {
950 goto copy
951 }
952 if q.Pcond == nil || (q.Pcond.Mark&FOLL != 0) {
953 continue
954 }
955 if a != ABEQ && a != ABNE {
956 continue
957 }
958
959 copy:
960 for {
961 r = ctxt.NewProg()
962 *r = *p
963 if r.Mark&FOLL == 0 {
964 fmt.Printf("can't happen 1\n")
965 }
966 r.Mark |= FOLL
967 if p != q {
968 p = p.Link
969 (*last).Link = r
970 *last = r
971 continue
972 }
973
974 (*last).Link = r
975 *last = r
976 if a == AB || (a == obj.ARET && q.Scond == C_SCOND_NONE) || a == ARFE || a == obj.AUNDEF {
977 return
978 }
979 r.As = ABNE
980 if a == ABNE {
981 r.As = ABEQ
982 }
983 r.Pcond = p.Link
984 r.Link = p.Pcond
985 if r.Link.Mark&FOLL == 0 {
986 xfol(ctxt, r.Link, last)
987 }
988 if r.Pcond.Mark&FOLL == 0 {
989 fmt.Printf("can't happen 2\n")
990 }
991 return
992 }
993 }
994
995 a = AB
996 q = ctxt.NewProg()
Dan Willemsen38f2dba2016-07-08 14:54:35 -0700997 q.As = a
Dan Willemsen09eb3b12015-09-16 14:34:17 -0700998 q.Lineno = p.Lineno
999 q.To.Type = obj.TYPE_BRANCH
1000 q.To.Offset = p.Pc
1001 q.Pcond = p
1002 p = q
1003 }
1004
1005 p.Mark |= FOLL
1006 (*last).Link = p
1007 *last = p
1008 if a == AB || (a == obj.ARET && p.Scond == C_SCOND_NONE) || a == ARFE || a == obj.AUNDEF {
1009 return
1010 }
1011
1012 if p.Pcond != nil {
1013 if a != ABL && a != ABX && p.Link != nil {
1014 q = obj.Brchain(ctxt, p.Link)
Dan Willemsen38f2dba2016-07-08 14:54:35 -07001015 if a != obj.ATEXT {
Dan Willemsen09eb3b12015-09-16 14:34:17 -07001016 if q != nil && (q.Mark&FOLL != 0) {
Dan Willemsen38f2dba2016-07-08 14:54:35 -07001017 p.As = relinv(a)
Dan Willemsen09eb3b12015-09-16 14:34:17 -07001018 p.Link = p.Pcond
1019 p.Pcond = q
1020 }
1021 }
1022
1023 xfol(ctxt, p.Link, last)
1024 q = obj.Brchain(ctxt, p.Pcond)
1025 if q == nil {
1026 q = p.Pcond
1027 }
1028 if q.Mark&FOLL != 0 {
1029 p.Pcond = q
1030 return
1031 }
1032
1033 p = q
1034 goto loop
1035 }
1036 }
1037
1038 p = p.Link
1039 goto loop
1040}
1041
Dan Willemsen38f2dba2016-07-08 14:54:35 -07001042var unaryDst = map[obj.As]bool{
Dan Willemsen09eb3b12015-09-16 14:34:17 -07001043 ASWI: true,
1044 AWORD: true,
1045}
1046
1047var Linkarm = obj.LinkArch{
Dan Willemsen38f2dba2016-07-08 14:54:35 -07001048 Arch: sys.ArchARM,
Dan Willemsen09eb3b12015-09-16 14:34:17 -07001049 Preprocess: preprocess,
1050 Assemble: span5,
1051 Follow: follow,
1052 Progedit: progedit,
1053 UnaryDst: unaryDst,
Dan Willemsen09eb3b12015-09-16 14:34:17 -07001054}