obj5.go 20 KB

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