defer.mx raw
1 package emit
2
3 import (
4 "math"
5 "git.smesh.lol/moxie/pkg/syntax"
6 "git.smesh.lol/moxie/pkg/ssa"
7 "git.smesh.lol/moxie/pkg/token"
8 "git.smesh.lol/moxie/pkg/types"
9 )
10
11 func (e *irEmitter) operandNoSideEffect(v ssa.SSAValue) string {
12 if v == nil {
13 return "zeroinitializer"
14 }
15 if c, ok := v.(*ssa.SSAConst); ok {
16 return e.constOperand(c)
17 }
18 if n, ok := e.valName[v]; ok {
19 return n
20 }
21 return ""
22 }
23
24 func (e *irEmitter) operand(v ssa.SSAValue) string {
25 if v == nil {
26 return "zeroinitializer"
27 }
28 if c, ok := v.(*ssa.SSAConst); ok {
29 return e.constOperand(c)
30 }
31 if b, ok := v.(*ssa.SSABuiltin); ok {
32 return "@runtime." | b.SSAName()
33 }
34 if f, ok := v.(*ssa.SSAFunction); ok {
35 e.declareExternalFunc(f)
36 return "{ ptr null, ptr " | e.funcSymbol(f) | " }"
37 }
38 if g, ok := v.(*ssa.SSAGlobal); ok {
39 e.declareExternalGlobal(g)
40 return e.globalName(g)
41 }
42 return e.regName(v)
43 }
44
45 func (e *irEmitter) constOperand(c *ssa.SSAConst) string {
46 if c.Value() == nil {
47 if c.SSAType() == nil {
48 return "null"
49 }
50 typ := e.llvmType(c.SSAType())
51 if typ == "ptr" {
52 return "null"
53 }
54 if typ == "i1" {
55 return "false"
56 }
57 return "zeroinitializer"
58 }
59 b := underlyingBasic(c.SSAType())
60 if b != nil {
61 switch b.Kind() {
62 case types.Bool, types.UntypedBool:
63 if cb, ok := c.Value().(*types.ConstBool); ok {
64 if cb.V {
65 return "true"
66 }
67 return "false"
68 }
69 s := c.Value().String()
70 if s == "true" {
71 return "true"
72 }
73 return "false"
74 case types.Int8, types.Int16, types.Int32, types.Int64, types.Uint8, types.Uint16, types.Uint32, types.Uint64,
75 types.UntypedInt, types.UntypedRune:
76 if ci, ok := c.Value().(*types.ConstInt); ok {
77 v := ci.V
78 switch b.Kind() {
79 case types.Int8:
80 v = int64(int8(v))
81 case types.Uint8:
82 v = int64(uint8(v))
83 case types.Int16:
84 v = int64(int16(v))
85 case types.Uint16:
86 v = int64(uint16(v))
87 case types.Int32:
88 v = int64(int32(v))
89 case types.Uint32:
90 v = int64(uint32(v))
91 case types.UntypedInt, types.UntypedRune:
92 if v < -2147483648 || v > 4294967295 {
93 e.allocTypes[c] = "i64"
94 }
95 }
96 return irItoa64(v)
97 }
98 if cf, ok := c.Value().(*types.ConstFloat); ok {
99 if len(cf.Lit) > 0 {
100 return irItoa64(parseIntFromTokenFloatLit(cf.Lit))
101 }
102 return irItoa64(int64(cf.V))
103 }
104 return c.Value().String()
105 case types.Float32:
106 if cf, ok := c.Value().(*types.ConstFloat); ok {
107 if len(cf.Lit) > 0 {
108 return floatLitForLLVM(cf.Lit)
109 }
110 bits := math.Float64bits(float64(float32(cf.V)))
111 return "0x" | irHex64(bits)
112 }
113 return "0.0"
114 case types.Float64, types.UntypedFloat:
115 if cf, ok := c.Value().(*types.ConstFloat); ok {
116 if len(cf.Lit) > 0 {
117 return floatLitForLLVM(cf.Lit)
118 }
119 bits := math.Float64bits(cf.V)
120 return "0x" | irHex64(bits)
121 }
122 return c.Value().String()
123 case types.TCString, types.UntypedString:
124 if cs, ok := c.Value().(*types.ConstStr); ok {
125 if len(cs.S) == 0 {
126 return "zeroinitializer"
127 }
128 idx := e.addStringConst(cs.S)
129 ipt := e.intptrType()
130 slen := irItoa64(int64(len(cs.S)))
131 return "{ ptr " | e.strConstGlobal(idx) | ", " | ipt | " " | slen | ", " | ipt | " " | slen | " }"
132 }
133 return "zeroinitializer"
134 }
135 }
136 if c.SSAType() == nil {
137 return c.Value().String()
138 }
139 return "zeroinitializer"
140 }
141
142 func underlyingBasic(t syntax.Type) *types.Basic {
143 if t == nil {
144 return nil
145 }
146 u := types.SafeUnderlying(t)
147 if u == nil {
148 return nil
149 }
150 b, ok := u.(*types.Basic)
151 if !ok {
152 return nil
153 }
154 return b
155 }
156
157 func newTCPackageWithUniverse(path, name string) *types.TCPackage {
158 return types.NewTCPackageWithParent(path, name, types.Universe)
159 }
160
161 func irParseIntWidth(t string) int32 {
162 if len(t) < 2 || t[0] != 'i' {
163 return 0
164 }
165 n := 0
166 for i := 1; i < len(t); i++ {
167 if t[i] < '0' || t[i] > '9' {
168 return 0
169 }
170 n = n*10 + int32(t[i]-'0')
171 }
172 return n
173 }
174
175 func (e *irEmitter) deferRetType(d *ssa.SSADefer) string {
176 if fn, ok := d.Call.Value.(*ssa.SSAFunction); ok {
177 if fn.Signature == nil {
178 return "void"
179 }
180 r := fn.Signature.Results()
181 if r == nil {
182 return "void"
183 }
184 return e.llvmType(r)
185 }
186 t := d.Call.Value.SSAType()
187 if t == nil {
188 return "void"
189 }
190 u := types.SafeUnderlying(t)
191 if sig, ok := u.(*types.Signature); ok {
192 r := sig.Results()
193 if r == nil || r.Len() == 0 {
194 return "void"
195 }
196 return e.llvmType(r)
197 }
198 return "void"
199 }
200
201 func (e *irEmitter) deferArgType(arg ssa.SSAValue) string {
202 t := e.llvmType(arg.SSAType())
203 if t == "void" {
204 return "ptr"
205 }
206 return t
207 }
208
209 func (e *irEmitter) deferIsDirectCall(d *ssa.SSADefer) bool {
210 if _, ok := d.Call.Value.(*ssa.SSAFunction); ok {
211 return true
212 }
213 if _, ok := d.Call.Value.(*ssa.SSABuiltin); ok {
214 return true
215 }
216 return false
217 }
218
219 func (e *irEmitter) deferStructType(d *ssa.SSADefer) string {
220 ipt := e.intptrType()
221 s := "{" | ipt | ", ptr"
222 if !e.deferIsDirectCall(d) {
223 s = s | ", {ptr, ptr}"
224 }
225 for _, arg := range d.Call.Args {
226 s = s | ", " | e.deferArgType(arg)
227 }
228 return s | "}"
229 }
230
231 func (e *irEmitter) emitDefer(d *ssa.SSADefer) {
232 idx := -1
233 for i, dd := range e.deferList {
234 if dd == d {
235 idx = i
236 break
237 }
238 }
239 if idx < 0 {
240 e.w(" ; defer: not found in list\n")
241 return
242 }
243 ipt := e.intptrType()
244 id := e.deferID
245 e.deferID++
246 pfx := "%df" | irItoa(id) | "."
247 sty := e.deferStructType(d)
248
249 e.w(" ") ; e.w(pfx) ; e.w("a = alloca ") ; e.w(sty) ; e.w("\n")
250
251 cbGep := pfx | "cb"
252 e.w(" ") ; e.w(cbGep) ; e.w(" = getelementptr inbounds ") ; e.w(sty)
253 e.w(", ptr ") ; e.w(pfx) ; e.w("a, i32 0, i32 0\n")
254 e.w(" store ") ; e.w(ipt) ; e.w(" ") ; e.w(irItoa(idx))
255 e.w(", ptr ") ; e.w(cbGep) ; e.w("\n")
256
257 nxGep := pfx | "nx"
258 e.w(" ") ; e.w(nxGep) ; e.w(" = getelementptr inbounds ") ; e.w(sty)
259 e.w(", ptr ") ; e.w(pfx) ; e.w("a, i32 0, i32 1\n")
260 prev := pfx | "prev"
261 e.w(" ") ; e.w(prev) ; e.w(" = load ptr, ptr %deferPtr\n")
262 e.w(" store ptr ") ; e.w(prev) ; e.w(", ptr ") ; e.w(nxGep) ; e.w("\n")
263
264 fieldIdx := int32(2)
265 if !e.deferIsDirectCall(d) {
266 fvGep := pfx | "fv"
267 e.w(" ") ; e.w(fvGep) ; e.w(" = getelementptr inbounds ") ; e.w(sty)
268 e.w(", ptr ") ; e.w(pfx) ; e.w("a, i32 0, i32 ") ; e.w(irItoa(int32(fieldIdx))) ; e.w("\n")
269 fvOp := e.operand(d.Call.Value)
270 e.w(" store {ptr, ptr} ") ; e.w(fvOp)
271 e.w(", ptr ") ; e.w(fvGep) ; e.w("\n")
272 fieldIdx++
273 }
274
275 for i, arg := range d.Call.Args {
276 _ = i
277 aGep := pfx | "a" | irItoa(int32(fieldIdx))
278 e.w(" ") ; e.w(aGep) ; e.w(" = getelementptr inbounds ") ; e.w(sty)
279 e.w(", ptr ") ; e.w(pfx) ; e.w("a, i32 0, i32 ") ; e.w(irItoa(int32(fieldIdx))) ; e.w("\n")
280 at := e.deferArgType(arg)
281 av := e.operand(arg)
282 e.w(" store ") ; e.w(at) ; e.w(" ") ; e.w(av)
283 e.w(", ptr ") ; e.w(aGep) ; e.w("\n")
284 fieldIdx++
285 }
286
287 e.w(" store ptr ") ; e.w(pfx) ; e.w("a, ptr %deferPtr\n")
288 }
289
290 func (e *irEmitter) emitRunDefers() {
291 if len(e.deferList) == 0 {
292 return
293 }
294 ipt := e.intptrType()
295 id := e.deferID
296 e.deferID++
297 pfx := "rundefers" | irItoa(id)
298
299 e.w(" br label %") ; e.w(pfx) ; e.w(".head\n")
300 e.w(pfx) ; e.w(".head:\n")
301 e.w(" %") ; e.w(pfx) ; e.w(".cur = load ptr, ptr %deferPtr\n")
302 e.w(" %") ; e.w(pfx) ; e.w(".nil = icmp eq ptr %") ; e.w(pfx) ; e.w(".cur, null\n")
303 e.w(" br i1 %") ; e.w(pfx) ; e.w(".nil, label %") ; e.w(pfx) ; e.w(".end, label %")
304 e.w(pfx) ; e.w(".body\n")
305
306 e.w(pfx) ; e.w(".body:\n")
307 e.w(" %") ; e.w(pfx) ; e.w(".cb = load ") ; e.w(ipt) ; e.w(", ptr %") ; e.w(pfx) ; e.w(".cur\n")
308 nxGep := "%" | pfx | ".nxp"
309 e.w(" ") ; e.w(nxGep) ; e.w(" = getelementptr inbounds {") ; e.w(ipt) ; e.w(", ptr}, ptr %")
310 e.w(pfx) ; e.w(".cur, i32 0, i32 1\n")
311 e.w(" %") ; e.w(pfx) ; e.w(".nx = load ptr, ptr ") ; e.w(nxGep) ; e.w("\n")
312 e.w(" store ptr %") ; e.w(pfx) ; e.w(".nx, ptr %deferPtr\n")
313
314 for i, d := range e.deferList {
315 checkLabel := pfx | ".c" | irItoa(i)
316 callLabel := pfx | ".k" | irItoa(i)
317 var nextLabel string
318 if i+1 < len(e.deferList) {
319 nextLabel = pfx | ".c" | irItoa(i+1)
320 } else {
321 nextLabel = pfx | ".head"
322 }
323 if i == 0 {
324 e.w(" %") ; e.w(checkLabel) ; e.w(".eq = icmp eq ") ; e.w(ipt) ; e.w(" %")
325 e.w(pfx) ; e.w(".cb, 0\n")
326 e.w(" br i1 %") ; e.w(checkLabel) ; e.w(".eq, label %") ; e.w(callLabel)
327 e.w(", label %") ; e.w(nextLabel) ; e.w("\n")
328 } else {
329 e.w(checkLabel) ; e.w(":\n")
330 e.w(" %") ; e.w(checkLabel) ; e.w(".eq = icmp eq ") ; e.w(ipt) ; e.w(" %")
331 e.w(pfx) ; e.w(".cb, ") ; e.w(irItoa(i)) ; e.w("\n")
332 e.w(" br i1 %") ; e.w(checkLabel) ; e.w(".eq, label %") ; e.w(callLabel)
333 e.w(", label %") ; e.w(nextLabel) ; e.w("\n")
334 }
335
336 e.w(callLabel) ; e.w(":\n")
337 e.emitDeferDispatch(d, pfx, i)
338 e.w(" br label %") ; e.w(pfx) ; e.w(".head\n")
339 }
340
341 e.w(pfx) ; e.w(".end:\n")
342 }
343
344 func (e *irEmitter) emitDeferBuiltin(d *ssa.SSADefer, bi *ssa.SSABuiltin, pfx string, idx int32, sty string, dp string) {
345 fieldIdx := int32(2)
346 var args []string
347 var argTypes []string
348 for i, arg := range d.Call.Args {
349 aGep := "%" | pfx | ".da" | irItoa(idx) | "f" | irItoa(i)
350 e.w(" ") ; e.w(aGep) ; e.w(" = getelementptr inbounds ") ; e.w(sty)
351 e.w(", ptr ") ; e.w(dp) ; e.w(", i32 0, i32 ") ; e.w(irItoa(int32(fieldIdx))) ; e.w("\n")
352 at := e.deferArgType(arg)
353 aVal := "%" | pfx | ".dv" | irItoa(idx) | "f" | irItoa(i)
354 e.w(" ") ; e.w(aVal) ; e.w(" = load ") ; e.w(at) ; e.w(", ptr ") ; e.w(aGep) ; e.w("\n")
355 args = append(args, aVal)
356 argTypes = append(argTypes, at)
357 fieldIdx++
358 }
359 name := bi.SSAName()
360 if name == "close" && len(args) == 1 {
361 e.declareRuntime("runtime.chanClose", "void", "ptr")
362 e.w(" call void @runtime.chanClose(") ; e.w(argTypes[0]) ; e.w(" ") ; e.w(args[0]) ; e.w(")\n")
363 } else if name == "panic" && len(args) == 1 {
364 e.declareRuntime("runtime._panicstr", "void", argTypes[0])
365 e.w(" call void @runtime._panicstr(") ; e.w(argTypes[0]) ; e.w(" ") ; e.w(args[0]) ; e.w(")\n")
366 } else if name == "println" || name == "print" {
367 e.declareRuntime("runtime.printlock", "void", "")
368 e.declareRuntime("runtime.printunlock", "void", "")
369 e.declareRuntime("runtime.printnl", "void", "")
370 e.w(" call void @runtime.printlock()\n")
371 for ai, av := range args {
372 if ai > 0 {
373 e.declareRuntime("runtime.printspace", "void", "")
374 e.w(" call void @runtime.printspace()\n")
375 }
376 at := argTypes[ai]
377 if at == e.sliceType() || at == "{ptr, i64, i64}" || at == "{ptr, i32, i32}" {
378 e.declareRuntime("runtime.printstring", "void", e.sliceType())
379 e.w(" call void @runtime.printstring(") ; e.w(at) ; e.w(" ") ; e.w(av) ; e.w(")\n")
380 } else if at == "i1" {
381 e.declareRuntime("runtime.printbool", "void", "i1")
382 e.w(" call void @runtime.printbool(i1 ") ; e.w(av) ; e.w(")\n")
383 } else if at == "i32" || at == "i64" || at == "i16" || at == "i8" {
384 fname := "runtime.printint" | at[1:]
385 e.declareRuntime(fname, "void", at)
386 e.w(" call void @") ; e.w(fname) ; e.w("(") ; e.w(at) ; e.w(" ") ; e.w(av) ; e.w(")\n")
387 } else if at == "ptr" {
388 ipt := e.intptrType()
389 e.declareRuntime("runtime.printptr", "void", ipt)
390 tmp := "%" | pfx | ".ptmp" | irItoa(ai)
391 e.w(" ") ; e.w(tmp) ; e.w(" = ptrtoint ptr ") ; e.w(av) ; e.w(" to ") ; e.w(ipt) ; e.w("\n")
392 e.w(" call void @runtime.printptr(") ; e.w(ipt) ; e.w(" ") ; e.w(tmp) ; e.w(")\n")
393 }
394 }
395 if name == "println" {
396 e.w(" call void @runtime.printnl()\n")
397 }
398 e.w(" call void @runtime.printunlock()\n")
399 } else if name == "delete" && len(args) == 2 {
400 e.declareRuntime("runtime.hashmapBinaryDelete", "void", "ptr, ptr, i32")
401 e.w(" ; defer delete - not fully implemented\n")
402 } else if name == "recover" {
403 e.w(" ; defer recover() is a no-op\n")
404 } else {
405 e.w(" ; defer builtin ") ; e.w(name) ; e.w(" - not implemented\n")
406 }
407 }
408
409 func (e *irEmitter) emitDeferDispatch(d *ssa.SSADefer, pfx string, idx int32) {
410 sty := e.deferStructType(d)
411 dp := "%" | pfx | ".cur"
412
413 fieldIdx := int32(2)
414 if bi, isBi := d.Call.Value.(*ssa.SSABuiltin); isBi {
415 e.emitDeferBuiltin(d, bi, pfx, idx, sty, dp)
416 return
417 }
418 if fn, isFn := d.Call.Value.(*ssa.SSAFunction); isFn {
419 var args []string
420 var argTypes []string
421 for i, arg := range d.Call.Args {
422 aGep := "%" | pfx | ".da" | irItoa(idx) | "f" | irItoa(i)
423 e.w(" ") ; e.w(aGep) ; e.w(" = getelementptr inbounds ") ; e.w(sty)
424 e.w(", ptr ") ; e.w(dp) ; e.w(", i32 0, i32 ") ; e.w(irItoa(int32(fieldIdx))) ; e.w("\n")
425 at := e.deferArgType(arg)
426 aVal := "%" | pfx | ".dv" | irItoa(idx) | "f" | irItoa(i)
427 e.w(" ") ; e.w(aVal) ; e.w(" = load ") ; e.w(at) ; e.w(", ptr ") ; e.w(aGep) ; e.w("\n")
428 args = append(args, aVal)
429 argTypes = append(argTypes, at)
430 fieldIdx++
431 }
432 if !e.isPkgFunc(fn) {
433 e.declareExternalFunc(fn)
434 }
435 rt := e.deferRetType(d)
436 e.w(" call ") ; e.w(rt) ; e.w(" ") ; e.w(e.funcSymbol(fn)) ; e.w("(")
437 for i, av := range args {
438 if i > 0 { e.w(", ") }
439 e.w(argTypes[i]) ; e.w(" ") ; e.w(av)
440 }
441 if !fn.IsExternC() {
442 if len(args) > 0 { e.w(", ") }
443 e.w("ptr null")
444 }
445 e.w(")\n")
446 } else {
447 fvGep := "%" | pfx | ".dfv" | irItoa(idx)
448 e.w(" ") ; e.w(fvGep) ; e.w(" = getelementptr inbounds ") ; e.w(sty)
449 e.w(", ptr ") ; e.w(dp) ; e.w(", i32 0, i32 2\n")
450 fvVal := "%" | pfx | ".fvv" | irItoa(idx)
451 e.w(" ") ; e.w(fvVal) ; e.w(" = load {ptr, ptr}, ptr ") ; e.w(fvGep) ; e.w("\n")
452 fpReg := "%" | pfx | ".fp" | irItoa(idx)
453 ctxReg := "%" | pfx | ".ctx" | irItoa(idx)
454 e.w(" ") ; e.w(fpReg) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(fvVal) ; e.w(", 1\n")
455 e.w(" ") ; e.w(ctxReg) ; e.w(" = extractvalue {ptr, ptr} ") ; e.w(fvVal) ; e.w(", 0\n")
456 fieldIdx = 3
457 var args []string
458 var argTypes []string
459 for i, arg := range d.Call.Args {
460 aGep := "%" | pfx | ".da" | irItoa(idx) | "f" | irItoa(i)
461 e.w(" ") ; e.w(aGep) ; e.w(" = getelementptr inbounds ") ; e.w(sty)
462 e.w(", ptr ") ; e.w(dp) ; e.w(", i32 0, i32 ") ; e.w(irItoa(int32(fieldIdx))) ; e.w("\n")
463 at := e.deferArgType(arg)
464 aVal := "%" | pfx | ".dv" | irItoa(idx) | "f" | irItoa(i)
465 e.w(" ") ; e.w(aVal) ; e.w(" = load ") ; e.w(at) ; e.w(", ptr ") ; e.w(aGep) ; e.w("\n")
466 args = append(args, aVal)
467 argTypes = append(argTypes, at)
468 fieldIdx++
469 }
470 rt := e.deferRetType(d)
471 e.w(" call ") ; e.w(rt) ; e.w(" ") ; e.w(fpReg) ; e.w("(")
472 for i, av := range args {
473 if i > 0 { e.w(", ") }
474 e.w(argTypes[i]) ; e.w(" ") ; e.w(av)
475 }
476 if len(args) > 0 { e.w(", ") }
477 e.w("ptr ") ; e.w(ctxReg)
478 e.w(")\n")
479 }
480 }
481
482 func irItoa(n int32) string {
483 if n == 0 {
484 return "0"
485 }
486 neg := n < 0
487 if neg {
488 n = -n
489 }
490 buf := []byte{:0:20}
491 for n > 0 {
492 buf = append(buf, byte('0'+n%10))
493 n /= 10
494 }
495 if neg {
496 buf = append(buf, '-')
497 }
498 for i, j := 0, len(buf)-1; i < j; i, j = i+1, j-1 {
499 buf[i], buf[j] = buf[j], buf[i]
500 }
501 return string(buf)
502 }
503
504 func parseIntFromTokenFloatLit(lit string) int64 {
505 neg := false
506 i := 0
507 if len(lit) > 0 && lit[0] == '-' {
508 neg = true
509 i = 1
510 } else if len(lit) > 0 && lit[0] == '+' {
511 i = 1
512 }
513 var n int64
514 for ; i < len(lit); i++ {
515 ch := lit[i]
516 if ch == '_' {
517 continue
518 }
519 if ch < '0' || ch > '9' {
520 break
521 }
522 n = n*10 + int64(ch-'0')
523 }
524 if neg {
525 return -n
526 }
527 return n
528 }
529
530 func floatLitForLLVM(lit string) string {
531 var buf []byte
532 hasDot := false
533 ePos := -1
534 for i := 0; i < len(lit); i++ {
535 ch := lit[i]
536 if ch == '_' {
537 continue
538 }
539 if ch == '.' {
540 hasDot = true
541 if len(buf) == 0 || (len(buf) == 1 && (buf[0] == '-' || buf[0] == '+')) {
542 buf = append(buf, '0')
543 }
544 }
545 if ch == 'e' || ch == 'E' {
546 ePos = len(buf)
547 }
548 buf = append(buf, ch)
549 }
550 if !hasDot {
551 if ePos >= 0 {
552 var out []byte
553 out = append(out, buf[:ePos]...)
554 out = append(out, '.', '0')
555 out = append(out, buf[ePos:]...)
556 return string(out)
557 }
558 buf = append(buf, '.', '0')
559 }
560 return string(buf)
561 }
562
563 func irHex64(v uint64) string {
564 digits := "0123456789ABCDEF"
565 buf := []byte{:16}
566 for i := 15; i >= 0; i-- {
567 buf[i] = digits[v&0xf]
568 v >>= 4
569 }
570 return string(buf)
571 }
572
573 func irItoa64(n int64) string {
574 if n == 0 {
575 return "0"
576 }
577 neg := n < 0
578 if neg {
579 n = -n
580 }
581 if n < 0 {
582 return "-9223372036854775808"
583 }
584 buf := []byte{:0:20}
585 for n > 0 {
586 buf = append(buf, byte('0'+n%10))
587 n /= 10
588 }
589 if neg {
590 buf = append(buf, '-')
591 }
592 for i, j := 0, len(buf)-1; i < j; i, j = i+1, j-1 {
593 buf[i], buf[j] = buf[j], buf[i]
594 }
595 return string(buf)
596 }
597
598 func irFtoa(f float64) string {
599 return "0.0"
600 }
601
602 func irParseInt64(s string) int64 {
603 var n int64
604 for i := 0; i < len(s); i++ {
605 c := s[i]
606 if c < '0' || c > '9' {
607 break
608 }
609 n = n*10 + int64(c-'0')
610 }
611 return n
612 }
613
614 func runeToUTF8(r rune) string {
615 if r < 0 || r > 0x10FFFF {
616 r = 0xFFFD
617 }
618 var out []byte
619 if r <= 0x7F {
620 out = append(out, byte(r))
621 } else if r <= 0x7FF {
622 out = append(out, byte(0xC0|(r>>6)), byte(0x80|(r&0x3F)))
623 } else if r <= 0xFFFF {
624 out = append(out, byte(0xE0|(r>>12)), byte(0x80|((r>>6)&0x3F)), byte(0x80|(r&0x3F)))
625 } else {
626 out = append(out, byte(0xF0|(r>>18)), byte(0x80|((r>>12)&0x3F)), byte(0x80|((r>>6)&0x3F)), byte(0x80|(r&0x3F)))
627 }
628 return string(out)
629 }
630