assembler_amd64.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. /*
  2. * Copyright 2021 ByteDance Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package jit
  17. import (
  18. `encoding/binary`
  19. `strconv`
  20. `strings`
  21. `sync`
  22. `github.com/bytedance/sonic/loader`
  23. `github.com/bytedance/sonic/internal/rt`
  24. `github.com/twitchyliquid64/golang-asm/obj`
  25. `github.com/twitchyliquid64/golang-asm/obj/x86`
  26. )
  27. const (
  28. _LB_jump_pc = "_jump_pc_"
  29. )
  30. type BaseAssembler struct {
  31. i int
  32. f func()
  33. c []byte
  34. o sync.Once
  35. pb *Backend
  36. xrefs map[string][]*obj.Prog
  37. labels map[string]*obj.Prog
  38. pendings map[string][]*obj.Prog
  39. }
  40. /** Instruction Encoders **/
  41. var _NOPS = [][16]byte {
  42. {0x90}, // NOP
  43. {0x66, 0x90}, // 66 NOP
  44. {0x0f, 0x1f, 0x00}, // NOP DWORD ptr [EAX]
  45. {0x0f, 0x1f, 0x40, 0x00}, // NOP DWORD ptr [EAX + 00H]
  46. {0x0f, 0x1f, 0x44, 0x00, 0x00}, // NOP DWORD ptr [EAX + EAX*1 + 00H]
  47. {0x66, 0x0f, 0x1f, 0x44, 0x00, 0x00}, // 66 NOP DWORD ptr [EAX + EAX*1 + 00H]
  48. {0x0f, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x00}, // NOP DWORD ptr [EAX + 00000000H]
  49. {0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00}, // NOP DWORD ptr [EAX + EAX*1 + 00000000H]
  50. {0x66, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00}, // 66 NOP DWORD ptr [EAX + EAX*1 + 00000000H]
  51. }
  52. func (self *BaseAssembler) NOP() *obj.Prog {
  53. p := self.pb.New()
  54. p.As = obj.ANOP
  55. self.pb.Append(p)
  56. return p
  57. }
  58. func (self *BaseAssembler) NOPn(n int) {
  59. for i := len(_NOPS); i > 0 && n > 0; i-- {
  60. for ; n >= i; n -= i {
  61. self.Byte(_NOPS[i - 1][:i]...)
  62. }
  63. }
  64. }
  65. func (self *BaseAssembler) Byte(v ...byte) {
  66. for ; len(v) >= 8; v = v[8:] { self.From("QUAD", Imm(rt.Get64(v))) }
  67. for ; len(v) >= 4; v = v[4:] { self.From("LONG", Imm(int64(rt.Get32(v)))) }
  68. for ; len(v) >= 2; v = v[2:] { self.From("WORD", Imm(int64(rt.Get16(v)))) }
  69. for ; len(v) >= 1; v = v[1:] { self.From("BYTE", Imm(int64(v[0]))) }
  70. }
  71. func (self *BaseAssembler) Mark(pc int) {
  72. self.i++
  73. self.Link(_LB_jump_pc + strconv.Itoa(pc))
  74. }
  75. func (self *BaseAssembler) Link(to string) {
  76. var p *obj.Prog
  77. var v []*obj.Prog
  78. /* placeholder substitution */
  79. if strings.Contains(to, "{n}") {
  80. to = strings.ReplaceAll(to, "{n}", strconv.Itoa(self.i))
  81. }
  82. /* check for duplications */
  83. if _, ok := self.labels[to]; ok {
  84. panic("label " + to + " has already been linked")
  85. }
  86. /* get the pending links */
  87. p = self.NOP()
  88. v = self.pendings[to]
  89. /* patch all the pending jumps */
  90. for _, q := range v {
  91. q.To.Val = p
  92. }
  93. /* mark the label as resolved */
  94. self.labels[to] = p
  95. delete(self.pendings, to)
  96. }
  97. func (self *BaseAssembler) Xref(pc int, d int64) {
  98. self.Sref(_LB_jump_pc + strconv.Itoa(pc), d)
  99. }
  100. func (self *BaseAssembler) Sref(to string, d int64) {
  101. p := self.pb.New()
  102. p.As = x86.ALONG
  103. p.From = Imm(-d)
  104. /* placeholder substitution */
  105. if strings.Contains(to, "{n}") {
  106. to = strings.ReplaceAll(to, "{n}", strconv.Itoa(self.i))
  107. }
  108. /* record the patch point */
  109. self.pb.Append(p)
  110. self.xrefs[to] = append(self.xrefs[to], p)
  111. }
  112. func (self *BaseAssembler) Xjmp(op string, to int) {
  113. self.Sjmp(op, _LB_jump_pc + strconv.Itoa(to))
  114. }
  115. func (self *BaseAssembler) Sjmp(op string, to string) {
  116. p := self.pb.New()
  117. p.As = As(op)
  118. /* placeholder substitution */
  119. if strings.Contains(to, "{n}") {
  120. to = strings.ReplaceAll(to, "{n}", strconv.Itoa(self.i))
  121. }
  122. /* check for backward jumps */
  123. if v, ok := self.labels[to]; ok {
  124. p.To.Val = v
  125. } else {
  126. self.pendings[to] = append(self.pendings[to], p)
  127. }
  128. /* mark as a branch, and add to instruction buffer */
  129. p.To.Type = obj.TYPE_BRANCH
  130. self.pb.Append(p)
  131. }
  132. func (self *BaseAssembler) Rjmp(op string, to obj.Addr) {
  133. p := self.pb.New()
  134. p.To = to
  135. p.As = As(op)
  136. self.pb.Append(p)
  137. }
  138. func (self *BaseAssembler) From(op string, val obj.Addr) {
  139. p := self.pb.New()
  140. p.As = As(op)
  141. p.From = val
  142. self.pb.Append(p)
  143. }
  144. func (self *BaseAssembler) Emit(op string, args ...obj.Addr) {
  145. p := self.pb.New()
  146. p.As = As(op)
  147. self.assignOperands(p, args)
  148. self.pb.Append(p)
  149. }
  150. func (self *BaseAssembler) assignOperands(p *obj.Prog, args []obj.Addr) {
  151. switch len(args) {
  152. case 0 :
  153. case 1 : p.To = args[0]
  154. case 2 : p.To, p.From = args[1], args[0]
  155. case 3 : p.To, p.From, p.RestArgs = args[2], args[0], args[1:2]
  156. case 4 : p.To, p.From, p.RestArgs = args[2], args[3], args[:2]
  157. default : panic("invalid operands")
  158. }
  159. }
  160. /** Assembler Helpers **/
  161. func (self *BaseAssembler) Size() int {
  162. self.build()
  163. return len(self.c)
  164. }
  165. func (self *BaseAssembler) Init(f func()) {
  166. self.i = 0
  167. self.f = f
  168. self.c = nil
  169. self.o = sync.Once{}
  170. }
  171. var jitLoader = loader.Loader{
  172. Name: "sonic.jit.",
  173. File: "github.com/bytedance/sonic/jit.go",
  174. Options: loader.Options{
  175. NoPreempt: true,
  176. },
  177. }
  178. func (self *BaseAssembler) Load(name string, frameSize int, argSize int, argStackmap []bool, localStackmap []bool) loader.Function {
  179. self.build()
  180. return jitLoader.LoadOne(self.c, name, frameSize, argSize, argStackmap, localStackmap)
  181. }
  182. /** Assembler Stages **/
  183. func (self *BaseAssembler) init() {
  184. self.pb = newBackend("amd64")
  185. self.xrefs = map[string][]*obj.Prog{}
  186. self.labels = map[string]*obj.Prog{}
  187. self.pendings = map[string][]*obj.Prog{}
  188. }
  189. func (self *BaseAssembler) build() {
  190. self.o.Do(func() {
  191. self.init()
  192. self.f()
  193. self.validate()
  194. self.assemble()
  195. self.resolve()
  196. self.release()
  197. })
  198. }
  199. func (self *BaseAssembler) release() {
  200. self.pb.Release()
  201. self.pb = nil
  202. self.xrefs = nil
  203. self.labels = nil
  204. self.pendings = nil
  205. }
  206. func (self *BaseAssembler) resolve() {
  207. for s, v := range self.xrefs {
  208. for _, prog := range v {
  209. if prog.As != x86.ALONG {
  210. panic("invalid RIP relative reference")
  211. } else if p, ok := self.labels[s]; !ok {
  212. panic("links are not fully resolved: " + s)
  213. } else {
  214. off := prog.From.Offset + p.Pc - prog.Pc
  215. binary.LittleEndian.PutUint32(self.c[prog.Pc:], uint32(off))
  216. }
  217. }
  218. }
  219. }
  220. func (self *BaseAssembler) validate() {
  221. for key := range self.pendings {
  222. panic("links are not fully resolved: " + key)
  223. }
  224. }
  225. func (self *BaseAssembler) assemble() {
  226. self.c = self.pb.Assemble()
  227. }