objz.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735
  1. // Based on cmd/internal/obj/ppc64/obj9.go.
  2. //
  3. // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
  4. // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
  5. // Portions Copyright © 1997-1999 Vita Nuova Limited
  6. // Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com)
  7. // Portions Copyright © 2004,2006 Bruce Ellis
  8. // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
  9. // Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others
  10. // Portions Copyright © 2009 The Go Authors. All rights reserved.
  11. //
  12. // Permission is hereby granted, free of charge, to any person obtaining a copy
  13. // of this software and associated documentation files (the "Software"), to deal
  14. // in the Software without restriction, including without limitation the rights
  15. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  16. // copies of the Software, and to permit persons to whom the Software is
  17. // furnished to do so, subject to the following conditions:
  18. //
  19. // The above copyright notice and this permission notice shall be included in
  20. // all copies or substantial portions of the Software.
  21. //
  22. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  23. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  24. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  25. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  26. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  27. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  28. // THE SOFTWARE.
  29. package s390x
  30. import (
  31. "github.com/twitchyliquid64/golang-asm/obj"
  32. "github.com/twitchyliquid64/golang-asm/objabi"
  33. "github.com/twitchyliquid64/golang-asm/sys"
  34. "math"
  35. )
  36. func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
  37. p.From.Class = 0
  38. p.To.Class = 0
  39. c := ctxtz{ctxt: ctxt, newprog: newprog}
  40. // Rewrite BR/BL to symbol as TYPE_BRANCH.
  41. switch p.As {
  42. case ABR, ABL, obj.ARET, obj.ADUFFZERO, obj.ADUFFCOPY:
  43. if p.To.Sym != nil {
  44. p.To.Type = obj.TYPE_BRANCH
  45. }
  46. }
  47. // Rewrite float constants to values stored in memory unless they are +0.
  48. switch p.As {
  49. case AFMOVS:
  50. if p.From.Type == obj.TYPE_FCONST {
  51. f32 := float32(p.From.Val.(float64))
  52. if math.Float32bits(f32) == 0 { // +0
  53. break
  54. }
  55. p.From.Type = obj.TYPE_MEM
  56. p.From.Sym = ctxt.Float32Sym(f32)
  57. p.From.Name = obj.NAME_EXTERN
  58. p.From.Offset = 0
  59. }
  60. case AFMOVD:
  61. if p.From.Type == obj.TYPE_FCONST {
  62. f64 := p.From.Val.(float64)
  63. if math.Float64bits(f64) == 0 { // +0
  64. break
  65. }
  66. p.From.Type = obj.TYPE_MEM
  67. p.From.Sym = ctxt.Float64Sym(f64)
  68. p.From.Name = obj.NAME_EXTERN
  69. p.From.Offset = 0
  70. }
  71. // put constants not loadable by LOAD IMMEDIATE into memory
  72. case AMOVD:
  73. if p.From.Type == obj.TYPE_CONST {
  74. val := p.From.Offset
  75. if int64(int32(val)) != val &&
  76. int64(uint32(val)) != val &&
  77. int64(uint64(val)&(0xffffffff<<32)) != val {
  78. p.From.Type = obj.TYPE_MEM
  79. p.From.Sym = ctxt.Int64Sym(p.From.Offset)
  80. p.From.Name = obj.NAME_EXTERN
  81. p.From.Offset = 0
  82. }
  83. }
  84. }
  85. // Rewrite SUB constants into ADD.
  86. switch p.As {
  87. case ASUBC:
  88. if p.From.Type == obj.TYPE_CONST && isint32(-p.From.Offset) {
  89. p.From.Offset = -p.From.Offset
  90. p.As = AADDC
  91. }
  92. case ASUB:
  93. if p.From.Type == obj.TYPE_CONST && isint32(-p.From.Offset) {
  94. p.From.Offset = -p.From.Offset
  95. p.As = AADD
  96. }
  97. }
  98. if c.ctxt.Flag_dynlink {
  99. c.rewriteToUseGot(p)
  100. }
  101. }
  102. // Rewrite p, if necessary, to access global data via the global offset table.
  103. func (c *ctxtz) rewriteToUseGot(p *obj.Prog) {
  104. // At the moment EXRL instructions are not emitted by the compiler and only reference local symbols in
  105. // assembly code.
  106. if p.As == AEXRL {
  107. return
  108. }
  109. // We only care about global data: NAME_EXTERN means a global
  110. // symbol in the Go sense, and p.Sym.Local is true for a few
  111. // internally defined symbols.
  112. // Rewrites must not clobber flags and therefore cannot use the
  113. // ADD instruction.
  114. if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
  115. // MOVD $sym, Rx becomes MOVD sym@GOT, Rx
  116. // MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx or REGTMP2; MOVD $<off>(Rx or REGTMP2), Rx
  117. if p.To.Type != obj.TYPE_REG || p.As != AMOVD {
  118. c.ctxt.Diag("do not know how to handle LEA-type insn to non-register in %v with -dynlink", p)
  119. }
  120. p.From.Type = obj.TYPE_MEM
  121. p.From.Name = obj.NAME_GOTREF
  122. q := p
  123. if p.From.Offset != 0 {
  124. target := p.To.Reg
  125. if target == REG_R0 {
  126. // Cannot use R0 as input to address calculation.
  127. // REGTMP might be used by the assembler.
  128. p.To.Reg = REGTMP2
  129. }
  130. q = obj.Appendp(q, c.newprog)
  131. q.As = AMOVD
  132. q.From.Type = obj.TYPE_ADDR
  133. q.From.Offset = p.From.Offset
  134. q.From.Reg = p.To.Reg
  135. q.To.Type = obj.TYPE_REG
  136. q.To.Reg = target
  137. p.From.Offset = 0
  138. }
  139. }
  140. if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
  141. c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
  142. }
  143. var source *obj.Addr
  144. // MOVD sym, Ry becomes MOVD sym@GOT, REGTMP2; MOVD (REGTMP2), Ry
  145. // MOVD Ry, sym becomes MOVD sym@GOT, REGTMP2; MOVD Ry, (REGTMP2)
  146. // An addition may be inserted between the two MOVs if there is an offset.
  147. if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
  148. if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
  149. c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
  150. }
  151. source = &p.From
  152. } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
  153. source = &p.To
  154. } else {
  155. return
  156. }
  157. if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
  158. return
  159. }
  160. if source.Sym.Type == objabi.STLSBSS {
  161. return
  162. }
  163. if source.Type != obj.TYPE_MEM {
  164. c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
  165. }
  166. p1 := obj.Appendp(p, c.newprog)
  167. p2 := obj.Appendp(p1, c.newprog)
  168. p1.As = AMOVD
  169. p1.From.Type = obj.TYPE_MEM
  170. p1.From.Sym = source.Sym
  171. p1.From.Name = obj.NAME_GOTREF
  172. p1.To.Type = obj.TYPE_REG
  173. p1.To.Reg = REGTMP2
  174. p2.As = p.As
  175. p2.From = p.From
  176. p2.To = p.To
  177. if p.From.Name == obj.NAME_EXTERN {
  178. p2.From.Reg = REGTMP2
  179. p2.From.Name = obj.NAME_NONE
  180. p2.From.Sym = nil
  181. } else if p.To.Name == obj.NAME_EXTERN {
  182. p2.To.Reg = REGTMP2
  183. p2.To.Name = obj.NAME_NONE
  184. p2.To.Sym = nil
  185. } else {
  186. return
  187. }
  188. obj.Nopout(p)
  189. }
  190. func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
  191. // TODO(minux): add morestack short-cuts with small fixed frame-size.
  192. if cursym.Func.Text == nil || cursym.Func.Text.Link == nil {
  193. return
  194. }
  195. c := ctxtz{ctxt: ctxt, cursym: cursym, newprog: newprog}
  196. p := c.cursym.Func.Text
  197. textstksiz := p.To.Offset
  198. if textstksiz == -8 {
  199. // Compatibility hack.
  200. p.From.Sym.Set(obj.AttrNoFrame, true)
  201. textstksiz = 0
  202. }
  203. if textstksiz%8 != 0 {
  204. c.ctxt.Diag("frame size %d not a multiple of 8", textstksiz)
  205. }
  206. if p.From.Sym.NoFrame() {
  207. if textstksiz != 0 {
  208. c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
  209. }
  210. }
  211. c.cursym.Func.Args = p.To.Val.(int32)
  212. c.cursym.Func.Locals = int32(textstksiz)
  213. /*
  214. * find leaf subroutines
  215. * strip NOPs
  216. * expand RET
  217. */
  218. var q *obj.Prog
  219. for p := c.cursym.Func.Text; p != nil; p = p.Link {
  220. switch p.As {
  221. case obj.ATEXT:
  222. q = p
  223. p.Mark |= LEAF
  224. case ABL, ABCL:
  225. q = p
  226. c.cursym.Func.Text.Mark &^= LEAF
  227. fallthrough
  228. case ABC,
  229. ABRC,
  230. ABEQ,
  231. ABGE,
  232. ABGT,
  233. ABLE,
  234. ABLT,
  235. ABLEU,
  236. ABLTU,
  237. ABNE,
  238. ABR,
  239. ABVC,
  240. ABVS,
  241. ACRJ,
  242. ACGRJ,
  243. ACLRJ,
  244. ACLGRJ,
  245. ACIJ,
  246. ACGIJ,
  247. ACLIJ,
  248. ACLGIJ,
  249. ACMPBEQ,
  250. ACMPBGE,
  251. ACMPBGT,
  252. ACMPBLE,
  253. ACMPBLT,
  254. ACMPBNE,
  255. ACMPUBEQ,
  256. ACMPUBGE,
  257. ACMPUBGT,
  258. ACMPUBLE,
  259. ACMPUBLT,
  260. ACMPUBNE:
  261. q = p
  262. p.Mark |= BRANCH
  263. default:
  264. q = p
  265. }
  266. }
  267. autosize := int32(0)
  268. var pLast *obj.Prog
  269. var pPre *obj.Prog
  270. var pPreempt *obj.Prog
  271. wasSplit := false
  272. for p := c.cursym.Func.Text; p != nil; p = p.Link {
  273. pLast = p
  274. switch p.As {
  275. case obj.ATEXT:
  276. autosize = int32(textstksiz)
  277. if p.Mark&LEAF != 0 && autosize == 0 {
  278. // A leaf function with no locals has no frame.
  279. p.From.Sym.Set(obj.AttrNoFrame, true)
  280. }
  281. if !p.From.Sym.NoFrame() {
  282. // If there is a stack frame at all, it includes
  283. // space to save the LR.
  284. autosize += int32(c.ctxt.FixedFrameSize())
  285. }
  286. if p.Mark&LEAF != 0 && autosize < objabi.StackSmall {
  287. // A leaf function with a small stack can be marked
  288. // NOSPLIT, avoiding a stack check.
  289. p.From.Sym.Set(obj.AttrNoSplit, true)
  290. }
  291. p.To.Offset = int64(autosize)
  292. q := p
  293. if !p.From.Sym.NoSplit() {
  294. p, pPreempt = c.stacksplitPre(p, autosize) // emit pre part of split check
  295. pPre = p
  296. p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
  297. wasSplit = true //need post part of split
  298. }
  299. if autosize != 0 {
  300. // Make sure to save link register for non-empty frame, even if
  301. // it is a leaf function, so that traceback works.
  302. // Store link register before decrementing SP, so if a signal comes
  303. // during the execution of the function prologue, the traceback
  304. // code will not see a half-updated stack frame.
  305. // This sequence is not async preemptible, as if we open a frame
  306. // at the current SP, it will clobber the saved LR.
  307. q = c.ctxt.StartUnsafePoint(p, c.newprog)
  308. q = obj.Appendp(q, c.newprog)
  309. q.As = AMOVD
  310. q.From.Type = obj.TYPE_REG
  311. q.From.Reg = REG_LR
  312. q.To.Type = obj.TYPE_MEM
  313. q.To.Reg = REGSP
  314. q.To.Offset = int64(-autosize)
  315. q = obj.Appendp(q, c.newprog)
  316. q.As = AMOVD
  317. q.From.Type = obj.TYPE_ADDR
  318. q.From.Offset = int64(-autosize)
  319. q.From.Reg = REGSP // not actually needed - REGSP is assumed if no reg is provided
  320. q.To.Type = obj.TYPE_REG
  321. q.To.Reg = REGSP
  322. q.Spadj = autosize
  323. q = c.ctxt.EndUnsafePoint(q, c.newprog, -1)
  324. } else if c.cursym.Func.Text.Mark&LEAF == 0 {
  325. // A very few functions that do not return to their caller
  326. // (e.g. gogo) are not identified as leaves but still have
  327. // no frame.
  328. c.cursym.Func.Text.Mark |= LEAF
  329. }
  330. if c.cursym.Func.Text.Mark&LEAF != 0 {
  331. c.cursym.Set(obj.AttrLeaf, true)
  332. break
  333. }
  334. if c.cursym.Func.Text.From.Sym.Wrapper() {
  335. // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
  336. //
  337. // MOVD g_panic(g), R3
  338. // CMP R3, $0
  339. // BEQ end
  340. // MOVD panic_argp(R3), R4
  341. // ADD $(autosize+8), R1, R5
  342. // CMP R4, R5
  343. // BNE end
  344. // ADD $8, R1, R6
  345. // MOVD R6, panic_argp(R3)
  346. // end:
  347. // NOP
  348. //
  349. // The NOP is needed to give the jumps somewhere to land.
  350. // It is a liblink NOP, not a s390x NOP: it encodes to 0 instruction bytes.
  351. q = obj.Appendp(q, c.newprog)
  352. q.As = AMOVD
  353. q.From.Type = obj.TYPE_MEM
  354. q.From.Reg = REGG
  355. q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
  356. q.To.Type = obj.TYPE_REG
  357. q.To.Reg = REG_R3
  358. q = obj.Appendp(q, c.newprog)
  359. q.As = ACMP
  360. q.From.Type = obj.TYPE_REG
  361. q.From.Reg = REG_R3
  362. q.To.Type = obj.TYPE_CONST
  363. q.To.Offset = 0
  364. q = obj.Appendp(q, c.newprog)
  365. q.As = ABEQ
  366. q.To.Type = obj.TYPE_BRANCH
  367. p1 := q
  368. q = obj.Appendp(q, c.newprog)
  369. q.As = AMOVD
  370. q.From.Type = obj.TYPE_MEM
  371. q.From.Reg = REG_R3
  372. q.From.Offset = 0 // Panic.argp
  373. q.To.Type = obj.TYPE_REG
  374. q.To.Reg = REG_R4
  375. q = obj.Appendp(q, c.newprog)
  376. q.As = AADD
  377. q.From.Type = obj.TYPE_CONST
  378. q.From.Offset = int64(autosize) + c.ctxt.FixedFrameSize()
  379. q.Reg = REGSP
  380. q.To.Type = obj.TYPE_REG
  381. q.To.Reg = REG_R5
  382. q = obj.Appendp(q, c.newprog)
  383. q.As = ACMP
  384. q.From.Type = obj.TYPE_REG
  385. q.From.Reg = REG_R4
  386. q.To.Type = obj.TYPE_REG
  387. q.To.Reg = REG_R5
  388. q = obj.Appendp(q, c.newprog)
  389. q.As = ABNE
  390. q.To.Type = obj.TYPE_BRANCH
  391. p2 := q
  392. q = obj.Appendp(q, c.newprog)
  393. q.As = AADD
  394. q.From.Type = obj.TYPE_CONST
  395. q.From.Offset = c.ctxt.FixedFrameSize()
  396. q.Reg = REGSP
  397. q.To.Type = obj.TYPE_REG
  398. q.To.Reg = REG_R6
  399. q = obj.Appendp(q, c.newprog)
  400. q.As = AMOVD
  401. q.From.Type = obj.TYPE_REG
  402. q.From.Reg = REG_R6
  403. q.To.Type = obj.TYPE_MEM
  404. q.To.Reg = REG_R3
  405. q.To.Offset = 0 // Panic.argp
  406. q = obj.Appendp(q, c.newprog)
  407. q.As = obj.ANOP
  408. p1.To.SetTarget(q)
  409. p2.To.SetTarget(q)
  410. }
  411. case obj.ARET:
  412. retTarget := p.To.Sym
  413. if c.cursym.Func.Text.Mark&LEAF != 0 {
  414. if autosize == 0 {
  415. p.As = ABR
  416. p.From = obj.Addr{}
  417. if retTarget == nil {
  418. p.To.Type = obj.TYPE_REG
  419. p.To.Reg = REG_LR
  420. } else {
  421. p.To.Type = obj.TYPE_BRANCH
  422. p.To.Sym = retTarget
  423. }
  424. p.Mark |= BRANCH
  425. break
  426. }
  427. p.As = AADD
  428. p.From.Type = obj.TYPE_CONST
  429. p.From.Offset = int64(autosize)
  430. p.To.Type = obj.TYPE_REG
  431. p.To.Reg = REGSP
  432. p.Spadj = -autosize
  433. q = obj.Appendp(p, c.newprog)
  434. q.As = ABR
  435. q.From = obj.Addr{}
  436. q.To.Type = obj.TYPE_REG
  437. q.To.Reg = REG_LR
  438. q.Mark |= BRANCH
  439. q.Spadj = autosize
  440. break
  441. }
  442. p.As = AMOVD
  443. p.From.Type = obj.TYPE_MEM
  444. p.From.Reg = REGSP
  445. p.From.Offset = 0
  446. p.To.Type = obj.TYPE_REG
  447. p.To.Reg = REG_LR
  448. q = p
  449. if autosize != 0 {
  450. q = obj.Appendp(q, c.newprog)
  451. q.As = AADD
  452. q.From.Type = obj.TYPE_CONST
  453. q.From.Offset = int64(autosize)
  454. q.To.Type = obj.TYPE_REG
  455. q.To.Reg = REGSP
  456. q.Spadj = -autosize
  457. }
  458. q = obj.Appendp(q, c.newprog)
  459. q.As = ABR
  460. q.From = obj.Addr{}
  461. if retTarget == nil {
  462. q.To.Type = obj.TYPE_REG
  463. q.To.Reg = REG_LR
  464. } else {
  465. q.To.Type = obj.TYPE_BRANCH
  466. q.To.Sym = retTarget
  467. }
  468. q.Mark |= BRANCH
  469. q.Spadj = autosize
  470. case AADD:
  471. if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
  472. p.Spadj = int32(-p.From.Offset)
  473. }
  474. case obj.AGETCALLERPC:
  475. if cursym.Leaf() {
  476. /* MOVD LR, Rd */
  477. p.As = AMOVD
  478. p.From.Type = obj.TYPE_REG
  479. p.From.Reg = REG_LR
  480. } else {
  481. /* MOVD (RSP), Rd */
  482. p.As = AMOVD
  483. p.From.Type = obj.TYPE_MEM
  484. p.From.Reg = REGSP
  485. }
  486. }
  487. }
  488. if wasSplit {
  489. c.stacksplitPost(pLast, pPre, pPreempt, autosize) // emit post part of split check
  490. }
  491. }
  492. func (c *ctxtz) stacksplitPre(p *obj.Prog, framesize int32) (*obj.Prog, *obj.Prog) {
  493. var q *obj.Prog
  494. // MOVD g_stackguard(g), R3
  495. p = obj.Appendp(p, c.newprog)
  496. p.As = AMOVD
  497. p.From.Type = obj.TYPE_MEM
  498. p.From.Reg = REGG
  499. p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
  500. if c.cursym.CFunc() {
  501. p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
  502. }
  503. p.To.Type = obj.TYPE_REG
  504. p.To.Reg = REG_R3
  505. // Mark the stack bound check and morestack call async nonpreemptible.
  506. // If we get preempted here, when resumed the preemption request is
  507. // cleared, but we'll still call morestack, which will double the stack
  508. // unnecessarily. See issue #35470.
  509. p = c.ctxt.StartUnsafePoint(p, c.newprog)
  510. q = nil
  511. if framesize <= objabi.StackSmall {
  512. // small stack: SP < stackguard
  513. // CMPUBGE stackguard, SP, label-of-call-to-morestack
  514. p = obj.Appendp(p, c.newprog)
  515. //q1 = p
  516. p.From.Type = obj.TYPE_REG
  517. p.From.Reg = REG_R3
  518. p.Reg = REGSP
  519. p.As = ACMPUBGE
  520. p.To.Type = obj.TYPE_BRANCH
  521. } else if framesize <= objabi.StackBig {
  522. // large stack: SP-framesize < stackguard-StackSmall
  523. // ADD $-(framesize-StackSmall), SP, R4
  524. // CMPUBGE stackguard, R4, label-of-call-to-morestack
  525. p = obj.Appendp(p, c.newprog)
  526. p.As = AADD
  527. p.From.Type = obj.TYPE_CONST
  528. p.From.Offset = -(int64(framesize) - objabi.StackSmall)
  529. p.Reg = REGSP
  530. p.To.Type = obj.TYPE_REG
  531. p.To.Reg = REG_R4
  532. p = obj.Appendp(p, c.newprog)
  533. p.From.Type = obj.TYPE_REG
  534. p.From.Reg = REG_R3
  535. p.Reg = REG_R4
  536. p.As = ACMPUBGE
  537. p.To.Type = obj.TYPE_BRANCH
  538. } else {
  539. // Such a large stack we need to protect against wraparound.
  540. // If SP is close to zero:
  541. // SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
  542. // The +StackGuard on both sides is required to keep the left side positive:
  543. // SP is allowed to be slightly below stackguard. See stack.h.
  544. //
  545. // Preemption sets stackguard to StackPreempt, a very large value.
  546. // That breaks the math above, so we have to check for that explicitly.
  547. // // stackguard is R3
  548. // CMP R3, $StackPreempt
  549. // BEQ label-of-call-to-morestack
  550. // ADD $StackGuard, SP, R4
  551. // SUB R3, R4
  552. // MOVD $(framesize+(StackGuard-StackSmall)), TEMP
  553. // CMPUBGE TEMP, R4, label-of-call-to-morestack
  554. p = obj.Appendp(p, c.newprog)
  555. p.As = ACMP
  556. p.From.Type = obj.TYPE_REG
  557. p.From.Reg = REG_R3
  558. p.To.Type = obj.TYPE_CONST
  559. p.To.Offset = objabi.StackPreempt
  560. p = obj.Appendp(p, c.newprog)
  561. q = p
  562. p.As = ABEQ
  563. p.To.Type = obj.TYPE_BRANCH
  564. p = obj.Appendp(p, c.newprog)
  565. p.As = AADD
  566. p.From.Type = obj.TYPE_CONST
  567. p.From.Offset = int64(objabi.StackGuard)
  568. p.Reg = REGSP
  569. p.To.Type = obj.TYPE_REG
  570. p.To.Reg = REG_R4
  571. p = obj.Appendp(p, c.newprog)
  572. p.As = ASUB
  573. p.From.Type = obj.TYPE_REG
  574. p.From.Reg = REG_R3
  575. p.To.Type = obj.TYPE_REG
  576. p.To.Reg = REG_R4
  577. p = obj.Appendp(p, c.newprog)
  578. p.As = AMOVD
  579. p.From.Type = obj.TYPE_CONST
  580. p.From.Offset = int64(framesize) + int64(objabi.StackGuard) - objabi.StackSmall
  581. p.To.Type = obj.TYPE_REG
  582. p.To.Reg = REGTMP
  583. p = obj.Appendp(p, c.newprog)
  584. p.From.Type = obj.TYPE_REG
  585. p.From.Reg = REGTMP
  586. p.Reg = REG_R4
  587. p.As = ACMPUBGE
  588. p.To.Type = obj.TYPE_BRANCH
  589. }
  590. return p, q
  591. }
  592. func (c *ctxtz) stacksplitPost(p *obj.Prog, pPre *obj.Prog, pPreempt *obj.Prog, framesize int32) *obj.Prog {
  593. // Now we are at the end of the function, but logically
  594. // we are still in function prologue. We need to fix the
  595. // SP data and PCDATA.
  596. spfix := obj.Appendp(p, c.newprog)
  597. spfix.As = obj.ANOP
  598. spfix.Spadj = -framesize
  599. pcdata := c.ctxt.EmitEntryStackMap(c.cursym, spfix, c.newprog)
  600. pcdata = c.ctxt.StartUnsafePoint(pcdata, c.newprog)
  601. // MOVD LR, R5
  602. p = obj.Appendp(pcdata, c.newprog)
  603. pPre.To.SetTarget(p)
  604. p.As = AMOVD
  605. p.From.Type = obj.TYPE_REG
  606. p.From.Reg = REG_LR
  607. p.To.Type = obj.TYPE_REG
  608. p.To.Reg = REG_R5
  609. if pPreempt != nil {
  610. pPreempt.To.SetTarget(p)
  611. }
  612. // BL runtime.morestack(SB)
  613. p = obj.Appendp(p, c.newprog)
  614. p.As = ABL
  615. p.To.Type = obj.TYPE_BRANCH
  616. if c.cursym.CFunc() {
  617. p.To.Sym = c.ctxt.Lookup("runtime.morestackc")
  618. } else if !c.cursym.Func.Text.From.Sym.NeedCtxt() {
  619. p.To.Sym = c.ctxt.Lookup("runtime.morestack_noctxt")
  620. } else {
  621. p.To.Sym = c.ctxt.Lookup("runtime.morestack")
  622. }
  623. p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
  624. // BR start
  625. p = obj.Appendp(p, c.newprog)
  626. p.As = ABR
  627. p.To.Type = obj.TYPE_BRANCH
  628. p.To.SetTarget(c.cursym.Func.Text.Link)
  629. return p
  630. }
  631. var unaryDst = map[obj.As]bool{
  632. ASTCK: true,
  633. ASTCKC: true,
  634. ASTCKE: true,
  635. ASTCKF: true,
  636. ANEG: true,
  637. ANEGW: true,
  638. AVONE: true,
  639. AVZERO: true,
  640. }
  641. var Links390x = obj.LinkArch{
  642. Arch: sys.ArchS390X,
  643. Init: buildop,
  644. Preprocess: preprocess,
  645. Assemble: spanz,
  646. Progedit: progedit,
  647. UnaryDst: unaryDst,
  648. DWARFRegisters: S390XDWARFRegisters,
  649. }