1
16
17 package jit
18
19 import (
20 `encoding/binary`
21 `strconv`
22 `strings`
23 `sync`
24
25 `github.com/bytedance/sonic/loader`
26 `github.com/bytedance/sonic/internal/rt`
27 `github.com/twitchyliquid64/golang-asm/obj`
28 `github.com/twitchyliquid64/golang-asm/obj/x86`
29 )
30
31 const (
32 _LB_jump_pc = "_jump_pc_"
33 )
34
35 type BaseAssembler struct {
36 i int
37 f func()
38 c []byte
39 o sync.Once
40 pb *Backend
41 xrefs map[string][]*obj.Prog
42 labels map[string]*obj.Prog
43 pendings map[string][]*obj.Prog
44 }
45
46
47
48 var _NOPS = [][16]byte {
49 {0x90},
50 {0x66, 0x90},
51 {0x0f, 0x1f, 0x00},
52 {0x0f, 0x1f, 0x40, 0x00},
53 {0x0f, 0x1f, 0x44, 0x00, 0x00},
54 {0x66, 0x0f, 0x1f, 0x44, 0x00, 0x00},
55 {0x0f, 0x1f, 0x80, 0x00, 0x00, 0x00, 0x00},
56 {0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00},
57 {0x66, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00},
58 }
59
60 func (self *BaseAssembler) NOP() *obj.Prog {
61 p := self.pb.New()
62 p.As = obj.ANOP
63 self.pb.Append(p)
64 return p
65 }
66
67 func (self *BaseAssembler) NOPn(n int) {
68 for i := len(_NOPS); i > 0 && n > 0; i-- {
69 for ; n >= i; n -= i {
70 self.Byte(_NOPS[i - 1][:i]...)
71 }
72 }
73 }
74
75 func (self *BaseAssembler) Byte(v ...byte) {
76 for ; len(v) >= 8; v = v[8:] { self.From("QUAD", Imm(rt.Get64(v))) }
77 for ; len(v) >= 4; v = v[4:] { self.From("LONG", Imm(int64(rt.Get32(v)))) }
78 for ; len(v) >= 2; v = v[2:] { self.From("WORD", Imm(int64(rt.Get16(v)))) }
79 for ; len(v) >= 1; v = v[1:] { self.From("BYTE", Imm(int64(v[0]))) }
80 }
81
82 func (self *BaseAssembler) Mark(pc int) {
83 self.i++
84 self.Link(_LB_jump_pc + strconv.Itoa(pc))
85 }
86
87 func (self *BaseAssembler) Link(to string) {
88 var p *obj.Prog
89 var v []*obj.Prog
90
91
92 if strings.Contains(to, "{n}") {
93 to = strings.ReplaceAll(to, "{n}", strconv.Itoa(self.i))
94 }
95
96
97 if _, ok := self.labels[to]; ok {
98 panic("label " + to + " has already been linked")
99 }
100
101
102 p = self.NOP()
103 v = self.pendings[to]
104
105
106 for _, q := range v {
107 q.To.Val = p
108 }
109
110
111 self.labels[to] = p
112 delete(self.pendings, to)
113 }
114
115 func (self *BaseAssembler) Xref(pc int, d int64) {
116 self.Sref(_LB_jump_pc + strconv.Itoa(pc), d)
117 }
118
119 func (self *BaseAssembler) Sref(to string, d int64) {
120 p := self.pb.New()
121 p.As = x86.ALONG
122 p.From = Imm(-d)
123
124
125 if strings.Contains(to, "{n}") {
126 to = strings.ReplaceAll(to, "{n}", strconv.Itoa(self.i))
127 }
128
129
130 self.pb.Append(p)
131 self.xrefs[to] = append(self.xrefs[to], p)
132 }
133
134 func (self *BaseAssembler) Xjmp(op string, to int) {
135 self.Sjmp(op, _LB_jump_pc + strconv.Itoa(to))
136 }
137
138 func (self *BaseAssembler) Sjmp(op string, to string) {
139 p := self.pb.New()
140 p.As = As(op)
141
142
143 if strings.Contains(to, "{n}") {
144 to = strings.ReplaceAll(to, "{n}", strconv.Itoa(self.i))
145 }
146
147
148 if v, ok := self.labels[to]; ok {
149 p.To.Val = v
150 } else {
151 self.pendings[to] = append(self.pendings[to], p)
152 }
153
154
155 p.To.Type = obj.TYPE_BRANCH
156 self.pb.Append(p)
157 }
158
159 func (self *BaseAssembler) Rjmp(op string, to obj.Addr) {
160 p := self.pb.New()
161 p.To = to
162 p.As = As(op)
163 self.pb.Append(p)
164 }
165
166 func (self *BaseAssembler) From(op string, val obj.Addr) {
167 p := self.pb.New()
168 p.As = As(op)
169 p.From = val
170 self.pb.Append(p)
171 }
172
173 func (self *BaseAssembler) Emit(op string, args ...obj.Addr) {
174 p := self.pb.New()
175 p.As = As(op)
176 self.assignOperands(p, args)
177 self.pb.Append(p)
178 }
179
180 func (self *BaseAssembler) assignOperands(p *obj.Prog, args []obj.Addr) {
181 switch len(args) {
182 case 0 :
183 case 1 : p.To = args[0]
184 case 2 : p.To, p.From = args[1], args[0]
185 case 3 : p.To, p.From, p.RestArgs = args[2], args[0], args[1:2]
186 case 4 : p.To, p.From, p.RestArgs = args[2], args[3], args[:2]
187 default : panic("invalid operands")
188 }
189 }
190
191
192
193 func (self *BaseAssembler) Size() int {
194 self.build()
195 return len(self.c)
196 }
197
198 func (self *BaseAssembler) Init(f func()) {
199 self.i = 0
200 self.f = f
201 self.c = nil
202 self.o = sync.Once{}
203 }
204
205 var jitLoader = loader.Loader{
206 Name: "sonic.jit.",
207 File: "github.com/bytedance/sonic/jit.go",
208 Options: loader.Options{
209 NoPreempt: true,
210 },
211 }
212
213 func (self *BaseAssembler) Load(name string, frameSize int, argSize int, argStackmap []bool, localStackmap []bool) loader.Function {
214 self.build()
215 return jitLoader.LoadOne(self.c, name, frameSize, argSize, argStackmap, localStackmap)
216 }
217
218
219
220 func (self *BaseAssembler) init() {
221 self.pb = newBackend("amd64")
222 self.xrefs = map[string][]*obj.Prog{}
223 self.labels = map[string]*obj.Prog{}
224 self.pendings = map[string][]*obj.Prog{}
225 }
226
227 func (self *BaseAssembler) build() {
228 self.o.Do(func() {
229 self.init()
230 self.f()
231 self.validate()
232 self.assemble()
233 self.resolve()
234 self.release()
235 })
236 }
237
238 func (self *BaseAssembler) release() {
239 self.pb.Release()
240 self.pb = nil
241 self.xrefs = nil
242 self.labels = nil
243 self.pendings = nil
244 }
245
246 func (self *BaseAssembler) resolve() {
247 for s, v := range self.xrefs {
248 for _, prog := range v {
249 if prog.As != x86.ALONG {
250 panic("invalid RIP relative reference")
251 } else if p, ok := self.labels[s]; !ok {
252 panic("links are not fully resolved: " + s)
253 } else {
254 off := prog.From.Offset + p.Pc - prog.Pc
255 binary.LittleEndian.PutUint32(self.c[prog.Pc:], uint32(off))
256 }
257 }
258 }
259 }
260
261 func (self *BaseAssembler) validate() {
262 for key := range self.pendings {
263 panic("links are not fully resolved: " + key)
264 }
265 }
266
267 func (self *BaseAssembler) assemble() {
268 self.c = self.pb.Assemble()
269 }
270
View as plain text