1 package interp
2 3 // This file implements memory as used by interp in a reversible way.
4 // Each new function call creates a new layer which is merged in the parent on
5 // successful return and is thrown away when the function couldn't complete (in
6 // which case the function call is done at runtime).
7 // Memory is not typed, except that there is a difference between pointer and
8 // non-pointer data. A pointer always points to an object. This implies:
9 // * Nil pointers are zero, and are not considered a pointer.
10 // * Pointers for memory-mapped I/O point to numeric pointer values, and are
11 // thus not considered pointers but regular values. Dereferencing them cannot be
12 // done in interp and results in a revert.
13 //
14 // Right now the memory is assumed to be little endian. This will need an update
15 // for big endian architectures, if Moxie ever adds support for one.
16 17 import (
18 "encoding/binary"
19 "errors"
20 "fmt"
21 "math"
22 "math/big"
23 "strconv"
24 "strings"
25 26 "tinygo.org/x/go-llvm"
27 )
28 29 // An object is a memory buffer that may be an already existing global or a
30 // global created with runtime.alloc or the alloca instruction. If llvmGlobal is
31 // set, that's the global for this object, otherwise it needs to be created (if
32 // it is still reachable when the package initializer returns). The
33 // llvmLayoutType is not necessarily a complete type: it may need to be
34 // repeated (for example, for a slice value).
35 //
36 // Objects are copied in a memory view when they are stored to, to provide the
37 // ability to roll back interpreting a function.
38 type object struct {
39 llvmGlobal llvm.Value
40 llvmType llvm.Type // must match llvmGlobal.GlobalValueType() if both are set, may be unset if llvmGlobal is set
41 llvmLayoutType llvm.Type // LLVM type based on runtime.alloc layout parameter, if available
42 globalName string // name, if not yet created (not guaranteed to be the final name)
43 buffer value // buffer with value as given by interp, nil if external
44 size uint32 // must match buffer.len(), if available
45 align int // alignment of the object (may be 0 if unknown)
46 constant bool // true if this is a constant global
47 marked uint8 // 0 means unmarked, 1 means external read, 2 means external write
48 }
49 50 // clone() returns a cloned version of this object, for when an object needs to
51 // be written to for example.
52 func (obj object) clone() object {
53 if obj.buffer != nil {
54 obj.buffer = obj.buffer.clone()
55 }
56 return obj
57 }
58 59 // A memoryView is bound to a function activation. Loads are done from this view
60 // or a parent view (up to the *runner if it isn't included in a view). Stores
61 // copy the object to the current view.
62 //
63 // For details, see the README in the package.
64 type memoryView struct {
65 r *runner
66 parent *memoryView
67 objects map[uint32]object
68 69 // These instructions were added to runtime.initAll while interpreting a
70 // function. They are stored here in a list so they can be removed if the
71 // execution of the function needs to be rolled back.
72 instructions []llvm.Value
73 }
74 75 // extend integrates the changes done by the sub-memoryView into this memory
76 // view. This happens when a function is successfully interpreted and returns to
77 // the parent, in which case all changed objects should be included in this
78 // memory view.
79 func (mv *memoryView) extend(sub memoryView) {
80 if mv.objects == nil && len(sub.objects) != 0 {
81 mv.objects = make(map[uint32]object)
82 }
83 for key, value := range sub.objects {
84 mv.objects[key] = value
85 }
86 mv.instructions = append(mv.instructions, sub.instructions...)
87 }
88 89 // revert undoes changes done in this memory view: it removes all instructions
90 // created in this memoryView. Do not reuse this memoryView.
91 func (mv *memoryView) revert() {
92 // Erase instructions in reverse order.
93 for i := len(mv.instructions) - 1; i >= 0; i-- {
94 llvmInst := mv.instructions[i]
95 if llvmInst.IsAInstruction().IsNil() {
96 // The IR builder will try to create constant versions of
97 // instructions whenever possible. If it does this, it's not an
98 // instruction and thus shouldn't be removed.
99 continue
100 }
101 llvmInst.EraseFromParentAsInstruction()
102 }
103 }
104 105 // markExternalLoad marks the given LLVM value as having an external read. That
106 // means that the interpreter can still read from it, but cannot write to it as
107 // that would mean the external read (done at runtime) reads from a state that
108 // would not exist had the whole initialization been done at runtime.
109 func (mv *memoryView) markExternalLoad(llvmValue llvm.Value) error {
110 return mv.markExternal(llvmValue, 1)
111 }
112 113 // markExternalStore marks the given LLVM value as having an external write.
114 // This means that the interpreter can no longer read from it or write to it, as
115 // that would happen in a different order than if all initialization were
116 // happening at runtime.
117 func (mv *memoryView) markExternalStore(llvmValue llvm.Value) error {
118 return mv.markExternal(llvmValue, 2)
119 }
120 121 // markExternal is a helper for markExternalLoad and markExternalStore, and
122 // should not be called directly.
123 func (mv *memoryView) markExternal(llvmValue llvm.Value, mark uint8) error {
124 if llvmValue.IsUndef() || llvmValue.IsNull() {
125 // Null and undef definitely don't contain (valid) pointers.
126 return nil
127 }
128 if !llvmValue.IsAInstruction().IsNil() || !llvmValue.IsAArgument().IsNil() {
129 // These are considered external by default, there is nothing to mark.
130 return nil
131 }
132 133 if !llvmValue.IsAGlobalValue().IsNil() {
134 objectIndex := mv.r.getValue(llvmValue).(pointerValue).index()
135 obj := mv.get(objectIndex)
136 if obj.marked < mark {
137 obj = obj.clone()
138 obj.marked = mark
139 if mv.objects == nil {
140 mv.objects = make(map[uint32]object)
141 }
142 mv.objects[objectIndex] = obj
143 if !llvmValue.IsAGlobalVariable().IsNil() {
144 initializer := llvmValue.Initializer()
145 if !initializer.IsNil() {
146 // Using mark '2' (which means read/write access) because
147 // even from an object that is only read from, the resulting
148 // loaded pointer can be written to.
149 err := mv.markExternal(initializer, 2)
150 if err != nil {
151 return err
152 }
153 }
154 } else {
155 // This is a function. Go through all instructions and mark all
156 // objects in there.
157 for bb := llvmValue.FirstBasicBlock(); !bb.IsNil(); bb = llvm.NextBasicBlock(bb) {
158 for inst := bb.FirstInstruction(); !inst.IsNil(); inst = llvm.NextInstruction(inst) {
159 opcode := inst.InstructionOpcode()
160 if opcode == llvm.Call {
161 calledValue := inst.CalledValue()
162 if !calledValue.IsAFunction().IsNil() {
163 functionName := calledValue.Name()
164 if functionName == "llvm.dbg.value" || strings.HasPrefix(functionName, "llvm.lifetime.") {
165 continue
166 }
167 }
168 }
169 if opcode == llvm.Br || opcode == llvm.Switch {
170 // These don't affect memory. Skipped here because
171 // they also have a label as operand.
172 continue
173 }
174 numOperands := inst.OperandsCount()
175 for i := 0; i < numOperands; i++ {
176 // Using mark '2' (which means read/write access)
177 // because this might be a store instruction.
178 err := mv.markExternal(inst.Operand(i), 2)
179 if err != nil {
180 return err
181 }
182 }
183 }
184 }
185 }
186 }
187 } else if !llvmValue.IsAConstantExpr().IsNil() {
188 switch llvmValue.Opcode() {
189 case llvm.IntToPtr, llvm.PtrToInt, llvm.BitCast, llvm.GetElementPtr:
190 err := mv.markExternal(llvmValue.Operand(0), mark)
191 if err != nil {
192 return err
193 }
194 case llvm.Add, llvm.Sub, llvm.Mul, llvm.UDiv, llvm.SDiv, llvm.URem, llvm.SRem, llvm.Shl, llvm.LShr, llvm.AShr, llvm.And, llvm.Or, llvm.Xor:
195 // Integer binary operators. Mark both operands.
196 err := mv.markExternal(llvmValue.Operand(0), mark)
197 if err != nil {
198 return err
199 }
200 err = mv.markExternal(llvmValue.Operand(1), mark)
201 if err != nil {
202 return err
203 }
204 default:
205 return fmt.Errorf("interp: unknown constant expression '%s'", instructionNameMap[llvmValue.Opcode()])
206 }
207 } else if !llvmValue.IsAInlineAsm().IsNil() {
208 // Inline assembly can modify globals but only exported globals. Let's
209 // hope the author knows what they're doing.
210 } else {
211 llvmType := llvmValue.Type()
212 switch llvmType.TypeKind() {
213 case llvm.IntegerTypeKind, llvm.FloatTypeKind, llvm.DoubleTypeKind:
214 // Nothing to do here. Integers and floats aren't pointers so don't
215 // need any marking.
216 case llvm.StructTypeKind:
217 numElements := llvmType.StructElementTypesCount()
218 for i := 0; i < numElements; i++ {
219 element := mv.r.builder.CreateExtractValue(llvmValue, i, "")
220 err := mv.markExternal(element, mark)
221 if err != nil {
222 return err
223 }
224 }
225 case llvm.ArrayTypeKind:
226 numElements := llvmType.ArrayLength()
227 for i := 0; i < numElements; i++ {
228 element := mv.r.builder.CreateExtractValue(llvmValue, i, "")
229 err := mv.markExternal(element, mark)
230 if err != nil {
231 return err
232 }
233 }
234 default:
235 return errors.New("interp: unknown type kind in markExternalValue")
236 }
237 }
238 return nil
239 }
240 241 // hasExternalLoadOrStore returns true if this object has an external load or
242 // store. If this has happened, it is not possible for the interpreter to load
243 // from the object or store to it without affecting the behavior of the program.
244 func (mv *memoryView) hasExternalLoadOrStore(v pointerValue) bool {
245 obj := mv.get(v.index())
246 return obj.marked >= 1
247 }
248 249 // hasExternalStore returns true if this object has an external store. If this
250 // is true, stores to this object are no longer allowed by the interpreter.
251 // It returns false if it only has an external load, in which case it is still
252 // possible for the interpreter to read from the object.
253 func (mv *memoryView) hasExternalStore(v pointerValue) bool {
254 obj := mv.get(v.index())
255 return obj.marked >= 2 && !obj.constant
256 }
257 258 // get returns an object that can only be read from, as it may return an object
259 // of a parent view.
260 func (mv *memoryView) get(index uint32) object {
261 if obj, ok := mv.objects[index]; ok {
262 return obj
263 }
264 if mv.parent != nil {
265 return mv.parent.get(index)
266 }
267 return mv.r.objects[index]
268 }
269 270 // getWritable returns an object that can be written to.
271 func (mv *memoryView) getWritable(index uint32) object {
272 if obj, ok := mv.objects[index]; ok {
273 // Object is already in the current memory view, so can be modified.
274 return obj
275 }
276 // Object is not currently in this view. Get it, and clone it for use.
277 obj := mv.get(index).clone()
278 mv.r.objects[index] = obj
279 return obj
280 }
281 282 // Replace the object (indicated with index) with the given object. This put is
283 // only done at the current memory view, so that if this memory view is reverted
284 // the object is not changed.
285 func (mv *memoryView) put(index uint32, obj object) {
286 if mv.objects == nil {
287 mv.objects = make(map[uint32]object)
288 }
289 if checks && mv.get(index).buffer == nil {
290 panic("writing to external object")
291 }
292 if checks && mv.get(index).buffer.len(mv.r) != obj.buffer.len(mv.r) {
293 panic("put() with a differently-sized object")
294 }
295 if checks && obj.constant {
296 panic("interp: store to a constant")
297 }
298 mv.objects[index] = obj
299 }
300 301 // Load the value behind the given pointer. Returns nil if the pointer points to
302 // an external global.
303 func (mv *memoryView) load(p pointerValue, size uint32) value {
304 if checks && mv.hasExternalStore(p) {
305 panic("interp: load from object with external store")
306 }
307 obj := mv.get(p.index())
308 if obj.buffer == nil {
309 // External global, return nil.
310 return nil
311 }
312 if p.offset() == 0 && size == obj.size {
313 return obj.buffer.clone()
314 }
315 if checks && p.offset()+size > obj.size {
316 panic("interp: load out of bounds")
317 }
318 v := obj.buffer.asRawValue(mv.r)
319 loadedValue := rawValue{
320 buf: v.buf[p.offset() : p.offset()+size],
321 }
322 return loadedValue
323 }
324 325 // Store to the value behind the given pointer. This overwrites the value in the
326 // memory view, so that the changed value is discarded when the memory view is
327 // reverted. Returns true on success, false if the object to store to is
328 // external.
329 func (mv *memoryView) store(v value, p pointerValue) bool {
330 if checks && mv.hasExternalLoadOrStore(p) {
331 panic("interp: store to object with external load/store")
332 }
333 obj := mv.get(p.index())
334 if obj.buffer == nil {
335 // External global, return false (for a failure).
336 return false
337 }
338 if checks && p.offset()+v.len(mv.r) > obj.size {
339 panic("interp: store out of bounds")
340 }
341 if p.offset() == 0 && v.len(mv.r) == obj.buffer.len(mv.r) {
342 obj.buffer = v
343 } else {
344 obj = obj.clone()
345 buffer := obj.buffer.asRawValue(mv.r)
346 obj.buffer = buffer
347 v := v.asRawValue(mv.r)
348 for i := uint32(0); i < v.len(mv.r); i++ {
349 buffer.buf[p.offset()+i] = v.buf[i]
350 }
351 }
352 mv.put(p.index(), obj)
353 return true // success
354 }
355 356 // value is some sort of value, comparable to a LLVM constant. It can be
357 // implemented in various ways for efficiency, but the fallback value (that all
358 // implementations can be converted to except for localValue) is rawValue.
359 type value interface {
360 // len returns the length in bytes.
361 len(r *runner) uint32
362 clone() value
363 asPointer(*runner) (pointerValue, error)
364 asRawValue(*runner) rawValue
365 Uint(*runner) uint64
366 Int(*runner) int64
367 toLLVMValue(llvm.Type, *memoryView) (llvm.Value, error)
368 String() string
369 }
370 371 // literalValue contains simple integer values that don't need to be stored in a
372 // buffer.
373 type literalValue struct {
374 value interface{}
375 }
376 377 // Make a literalValue given the number of bits.
378 func makeLiteralInt(value uint64, bits int) literalValue {
379 switch bits {
380 case 64:
381 return literalValue{value}
382 case 32:
383 return literalValue{uint32(value)}
384 case 16:
385 return literalValue{uint16(value)}
386 case 8:
387 return literalValue{uint8(value)}
388 default:
389 panic("unknown integer size")
390 }
391 }
392 393 func (v literalValue) len(r *runner) uint32 {
394 switch v.value.(type) {
395 case uint64:
396 return 8
397 case uint32:
398 return 4
399 case uint16:
400 return 2
401 case uint8:
402 return 1
403 default:
404 panic("unknown value type")
405 }
406 }
407 408 func (v literalValue) String() string {
409 // Note: passing a nil *runner to v.Int because we know it won't use it.
410 return strconv.FormatInt(v.Int(nil), 10)
411 }
412 413 func (v literalValue) clone() value {
414 return v
415 }
416 417 func (v literalValue) asPointer(r *runner) (pointerValue, error) {
418 return pointerValue{}, errIntegerAsPointer
419 }
420 421 func (v literalValue) asRawValue(r *runner) rawValue {
422 var buf []byte
423 switch value := v.value.(type) {
424 case uint64:
425 buf = make([]byte, 8)
426 r.byteOrder.PutUint64(buf, value)
427 case uint32:
428 buf = make([]byte, 4)
429 r.byteOrder.PutUint32(buf, uint32(value))
430 case uint16:
431 buf = make([]byte, 2)
432 r.byteOrder.PutUint16(buf, uint16(value))
433 case uint8:
434 buf = []byte{uint8(value)}
435 default:
436 panic("unknown value type")
437 }
438 raw := newRawValue(uint32(len(buf)))
439 for i, b := range buf {
440 raw.buf[i] = uint64(b)
441 }
442 return raw
443 }
444 445 func (v literalValue) Uint(r *runner) uint64 {
446 switch value := v.value.(type) {
447 case uint64:
448 return value
449 case uint32:
450 return uint64(value)
451 case uint16:
452 return uint64(value)
453 case uint8:
454 return uint64(value)
455 default:
456 panic("inpterp: unknown literal type")
457 }
458 }
459 460 func (v literalValue) Int(r *runner) int64 {
461 switch value := v.value.(type) {
462 case uint64:
463 return int64(value)
464 case uint32:
465 return int64(int32(value))
466 case uint16:
467 return int64(int16(value))
468 case uint8:
469 return int64(int8(value))
470 default:
471 panic("inpterp: unknown literal type")
472 }
473 }
474 475 func (v literalValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Value, error) {
476 switch llvmType.TypeKind() {
477 case llvm.IntegerTypeKind:
478 switch value := v.value.(type) {
479 case uint64:
480 return llvm.ConstInt(llvmType, value, false), nil
481 case uint32:
482 return llvm.ConstInt(llvmType, uint64(value), false), nil
483 case uint16:
484 return llvm.ConstInt(llvmType, uint64(value), false), nil
485 case uint8:
486 return llvm.ConstInt(llvmType, uint64(value), false), nil
487 default:
488 return llvm.Value{}, errors.New("interp: unknown literal type")
489 }
490 case llvm.DoubleTypeKind:
491 return llvm.ConstFloat(llvmType, math.Float64frombits(v.value.(uint64))), nil
492 case llvm.FloatTypeKind:
493 return llvm.ConstFloat(llvmType, float64(math.Float32frombits(v.value.(uint32)))), nil
494 default:
495 return v.asRawValue(mem.r).toLLVMValue(llvmType, mem)
496 }
497 }
498 499 // pointerValue contains a single pointer, with an offset into the underlying
500 // object.
501 type pointerValue struct {
502 pointer uint64 // low 32 bits are offset, high 32 bits are index
503 }
504 505 func newPointerValue(r *runner, index, offset int) pointerValue {
506 return pointerValue{
507 pointer: uint64(index)<<32 | uint64(offset),
508 }
509 }
510 511 func (v pointerValue) index() uint32 {
512 return uint32(v.pointer >> 32)
513 }
514 515 func (v pointerValue) offset() uint32 {
516 return uint32(v.pointer)
517 }
518 519 // addOffset essentially does a GEP operation (pointer arithmetic): it adds the
520 // offset to the pointer. It also checks that the offset doesn't overflow the
521 // maximum offset size (which is 4GB).
522 func (v pointerValue) addOffset(offset int64) (pointerValue, error) {
523 result := pointerValue{v.pointer + uint64(offset)}
524 if checks && v.index() != result.index() {
525 return result, fmt.Errorf("interp: offset %d out of range for object %v", offset, v)
526 }
527 return result, nil
528 }
529 530 func (v pointerValue) len(r *runner) uint32 {
531 return r.pointerSize
532 }
533 534 func (v pointerValue) String() string {
535 name := strconv.Itoa(int(v.index()))
536 if v.offset() == 0 {
537 return "<" + name + ">"
538 }
539 return "<" + name + "+" + strconv.Itoa(int(v.offset())) + ">"
540 }
541 542 func (v pointerValue) clone() value {
543 return v
544 }
545 546 func (v pointerValue) asPointer(r *runner) (pointerValue, error) {
547 return v, nil
548 }
549 550 func (v pointerValue) asRawValue(r *runner) rawValue {
551 rv := newRawValue(r.pointerSize)
552 for i := range rv.buf {
553 rv.buf[i] = v.pointer
554 }
555 return rv
556 }
557 558 func (v pointerValue) Uint(r *runner) uint64 {
559 panic("cannot convert pointer to integer")
560 }
561 562 func (v pointerValue) Int(r *runner) int64 {
563 panic("cannot convert pointer to integer")
564 }
565 566 func (v pointerValue) equal(rhs pointerValue) bool {
567 return v.pointer == rhs.pointer
568 }
569 570 func (v pointerValue) llvmValue(mem *memoryView) llvm.Value {
571 return mem.get(v.index()).llvmGlobal
572 }
573 574 // toLLVMValue returns the LLVM value for this pointer, which may be a GEP or
575 // bitcast. The llvm.Type parameter is optional, if omitted the pointer type may
576 // be different than expected.
577 func (v pointerValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Value, error) {
578 // If a particular LLVM type is requested, cast to it.
579 if !llvmType.IsNil() && llvmType.TypeKind() != llvm.PointerTypeKind {
580 // The LLVM value has (or should have) the same bytes once compiled, but
581 // does not have the right LLVM type. This can happen for example when
582 // storing to a struct with a single pointer field: this pointer may
583 // then become the value even though the pointer should be wrapped in a
584 // struct.
585 // This can be worked around by simply converting to a raw value,
586 // rawValue knows how to create such structs.
587 return v.asRawValue(mem.r).toLLVMValue(llvmType, mem)
588 }
589 590 // Obtain the llvmValue, creating it if it doesn't exist yet.
591 llvmValue := v.llvmValue(mem)
592 if llvmValue.IsNil() {
593 // The global does not yet exist. Probably this is the result of a
594 // runtime.alloc.
595 // First allocate a new global for this object.
596 obj := mem.get(v.index())
597 alignment := obj.align
598 if alignment == 0 {
599 // Unknown alignment, perhaps from a direct call to runtime.alloc in
600 // the runtime. Use a conservative default instead.
601 alignment = mem.r.maxAlign
602 }
603 if obj.llvmType.IsNil() && obj.llvmLayoutType.IsNil() {
604 // Create an initializer without knowing the global type.
605 // This is probably the result of a runtime.alloc call.
606 initializer, err := obj.buffer.asRawValue(mem.r).rawLLVMValue(mem)
607 if err != nil {
608 return llvm.Value{}, err
609 }
610 globalType := initializer.Type()
611 llvmValue = llvm.AddGlobal(mem.r.mod, globalType, obj.globalName)
612 llvmValue.SetInitializer(initializer)
613 llvmValue.SetAlignment(alignment)
614 obj.llvmGlobal = llvmValue
615 mem.put(v.index(), obj)
616 } else {
617 // The global type is known, or at least its structure.
618 var globalType llvm.Type
619 if !obj.llvmType.IsNil() {
620 // The exact type is known.
621 globalType = obj.llvmType
622 } else { // !obj.llvmLayoutType.IsNil()
623 // The exact type isn't known, but the object layout is known.
624 globalType = obj.llvmLayoutType
625 // The layout may not span the full size of the global because
626 // of repetition. One example would be make([]string, 5) which
627 // would be 10 words in size but the layout would only be two
628 // words (for the string type).
629 typeSize := mem.r.targetData.TypeAllocSize(globalType)
630 if typeSize != uint64(obj.size) {
631 globalType = llvm.ArrayType(globalType, int(uint64(obj.size)/typeSize))
632 }
633 }
634 if checks && mem.r.targetData.TypeAllocSize(globalType) != uint64(obj.size) {
635 panic("size of the globalType isn't the same as the object size")
636 }
637 llvmValue = llvm.AddGlobal(mem.r.mod, globalType, obj.globalName)
638 obj.llvmGlobal = llvmValue
639 mem.put(v.index(), obj)
640 641 // Set the initializer for the global. Do this after creation to avoid
642 // infinite recursion between creating the global and creating the
643 // contents of the global (if the global contains itself).
644 initializer, err := obj.buffer.toLLVMValue(globalType, mem)
645 if err != nil {
646 return llvm.Value{}, err
647 }
648 if checks && initializer.Type() != globalType {
649 return llvm.Value{}, errors.New("interp: allocated value does not match allocated type")
650 }
651 llvmValue.SetInitializer(initializer)
652 llvmValue.SetAlignment(alignment)
653 }
654 655 // It should be included in r.globals because otherwise markExternal
656 // would consider it a new global (and would fail to mark this global as
657 // having an externa load/store).
658 mem.r.globals[llvmValue] = int(v.index())
659 llvmValue.SetLinkage(llvm.InternalLinkage)
660 }
661 662 if v.offset() != 0 {
663 // If there is an offset, make sure to use a GEP to index into the
664 // pointer.
665 llvmValue = llvm.ConstInBoundsGEP(mem.r.mod.Context().Int8Type(), llvmValue, []llvm.Value{
666 llvm.ConstInt(mem.r.mod.Context().Int32Type(), uint64(v.offset()), false),
667 })
668 }
669 670 return llvmValue, nil
671 }
672 673 // rawValue is a raw memory buffer that can store either pointers or regular
674 // data. This is the fallback data for everything that isn't clearly a
675 // literalValue or pointerValue.
676 type rawValue struct {
677 // An integer in buf contains either pointers or bytes.
678 // If it is a byte, it is smaller than 256.
679 // If it is a pointer, the index is contained in the upper 32 bits and the
680 // offset is contained in the lower 32 bits.
681 buf []uint64
682 }
683 684 func newRawValue(size uint32) rawValue {
685 return rawValue{make([]uint64, size)}
686 }
687 688 func (v rawValue) len(r *runner) uint32 {
689 return uint32(len(v.buf))
690 }
691 692 func (v rawValue) String() string {
693 if len(v.buf) == 2 || len(v.buf) == 4 || len(v.buf) == 8 {
694 // Format as a pointer if the entire buf is this pointer.
695 if v.buf[0] > 255 {
696 isPointer := true
697 for _, p := range v.buf {
698 if p != v.buf[0] {
699 isPointer = false
700 break
701 }
702 }
703 if isPointer {
704 return pointerValue{v.buf[0]}.String()
705 }
706 }
707 // Format as number if none of the buf is a pointer.
708 if !v.hasPointer() {
709 // Construct a fake runner, which is little endian.
710 // We only use String() for debugging, so this is is good enough
711 // (the printed value will just be slightly wrong when debugging the
712 // interp package with GOOS=mips for example).
713 r := &runner{byteOrder: binary.LittleEndian}
714 return strconv.FormatInt(v.Int(r), 10)
715 }
716 }
717 return "<[…" + strconv.Itoa(len(v.buf)) + "]>"
718 }
719 720 func (v rawValue) clone() value {
721 newValue := v
722 newValue.buf = make([]uint64, len(v.buf))
723 copy(newValue.buf, v.buf)
724 return newValue
725 }
726 727 func (v rawValue) asPointer(r *runner) (pointerValue, error) {
728 if v.buf[0] <= 255 {
729 // Probably a null pointer or memory-mapped I/O.
730 return pointerValue{}, errIntegerAsPointer
731 }
732 return pointerValue{v.buf[0]}, nil
733 }
734 735 func (v rawValue) asRawValue(r *runner) rawValue {
736 return v
737 }
738 739 func (v rawValue) bytes() []byte {
740 buf := make([]byte, len(v.buf))
741 for i, p := range v.buf {
742 if p > 255 {
743 panic("cannot convert pointer value to byte")
744 }
745 buf[i] = byte(p)
746 }
747 return buf
748 }
749 750 func (v rawValue) Uint(r *runner) uint64 {
751 buf := v.bytes()
752 753 switch len(v.buf) {
754 case 1:
755 return uint64(buf[0])
756 case 2:
757 return uint64(r.byteOrder.Uint16(buf))
758 case 4:
759 return uint64(r.byteOrder.Uint32(buf))
760 case 8:
761 return r.byteOrder.Uint64(buf)
762 default:
763 panic("unknown integer size")
764 }
765 }
766 767 func (v rawValue) Int(r *runner) int64 {
768 switch len(v.buf) {
769 case 1:
770 return int64(int8(v.Uint(r)))
771 case 2:
772 return int64(int16(v.Uint(r)))
773 case 4:
774 return int64(int32(v.Uint(r)))
775 case 8:
776 return int64(int64(v.Uint(r)))
777 default:
778 panic("unknown integer size")
779 }
780 }
781 782 // equal returns true if (and only if) the value matches rhs.
783 func (v rawValue) equal(rhs rawValue) bool {
784 if len(v.buf) != len(rhs.buf) {
785 panic("comparing values of different size")
786 }
787 for i, p := range v.buf {
788 if rhs.buf[i] != p {
789 return false
790 }
791 }
792 return true
793 }
794 795 // rawLLVMValue returns a llvm.Value for this rawValue, making up a type as it
796 // goes. The resulting value does not have a specified type, but it will be the
797 // same size and have the same bytes if it was created with a provided LLVM type
798 // (through toLLVMValue).
799 func (v rawValue) rawLLVMValue(mem *memoryView) (llvm.Value, error) {
800 var structFields []llvm.Value
801 ctx := mem.r.mod.Context()
802 int8Type := ctx.Int8Type()
803 804 var bytesBuf []llvm.Value
805 // addBytes can be called after adding to bytesBuf to flush remaining bytes
806 // to a new array in structFields.
807 addBytes := func() {
808 if len(bytesBuf) == 0 {
809 return
810 }
811 if len(bytesBuf) == 1 {
812 structFields = append(structFields, bytesBuf[0])
813 } else {
814 structFields = append(structFields, llvm.ConstArray(int8Type, bytesBuf))
815 }
816 bytesBuf = nil
817 }
818 819 // Create structFields, converting the rawValue to a LLVM value.
820 for i := uint32(0); i < uint32(len(v.buf)); {
821 if v.buf[i] > 255 {
822 addBytes()
823 field, err := pointerValue{v.buf[i]}.toLLVMValue(llvm.Type{}, mem)
824 if err != nil {
825 return llvm.Value{}, err
826 }
827 structFields = append(structFields, field)
828 i += mem.r.pointerSize
829 continue
830 }
831 val := llvm.ConstInt(int8Type, uint64(v.buf[i]), false)
832 bytesBuf = append(bytesBuf, val)
833 i++
834 }
835 addBytes()
836 837 // Return the created data.
838 if len(structFields) == 1 {
839 return structFields[0], nil
840 }
841 return ctx.ConstStruct(structFields, false), nil
842 }
843 844 func (v rawValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Value, error) {
845 isZero := true
846 for _, p := range v.buf {
847 if p != 0 {
848 isZero = false
849 break
850 }
851 }
852 if isZero {
853 return llvm.ConstNull(llvmType), nil
854 }
855 switch llvmType.TypeKind() {
856 case llvm.IntegerTypeKind:
857 if v.buf[0] > 255 {
858 ptr, err := v.asPointer(mem.r)
859 if err != nil {
860 panic(err)
861 }
862 if checks && mem.r.targetData.TypeAllocSize(llvmType) != mem.r.targetData.TypeAllocSize(mem.r.dataPtrType) {
863 // Probably trying to serialize a pointer to a byte array,
864 // perhaps as a result of rawLLVMValue() in a previous interp
865 // run.
866 return llvm.Value{}, errInvalidPtrToIntSize
867 }
868 v, err := ptr.toLLVMValue(llvm.Type{}, mem)
869 if err != nil {
870 return llvm.Value{}, err
871 }
872 return llvm.ConstPtrToInt(v, llvmType), nil
873 }
874 var n uint64
875 switch llvmType.IntTypeWidth() {
876 case 64:
877 n = rawValue{v.buf[:8]}.Uint(mem.r)
878 case 32:
879 n = rawValue{v.buf[:4]}.Uint(mem.r)
880 case 16:
881 n = rawValue{v.buf[:2]}.Uint(mem.r)
882 case 8:
883 n = uint64(v.buf[0])
884 case 1:
885 n = uint64(v.buf[0])
886 if n != 0 && n != 1 {
887 panic("bool must be 0 or 1")
888 }
889 default:
890 panic("unknown integer size")
891 }
892 return llvm.ConstInt(llvmType, n, false), nil
893 case llvm.StructTypeKind:
894 fieldTypes := llvmType.StructElementTypes()
895 fields := make([]llvm.Value, len(fieldTypes))
896 for i, fieldType := range fieldTypes {
897 offset := mem.r.targetData.ElementOffset(llvmType, i)
898 field := rawValue{
899 buf: v.buf[offset:],
900 }
901 var err error
902 fields[i], err = field.toLLVMValue(fieldType, mem)
903 if err != nil {
904 return llvm.Value{}, err
905 }
906 }
907 if llvmType.StructName() != "" {
908 return llvm.ConstNamedStruct(llvmType, fields), nil
909 }
910 return llvmType.Context().ConstStruct(fields, false), nil
911 case llvm.ArrayTypeKind:
912 numElements := llvmType.ArrayLength()
913 childType := llvmType.ElementType()
914 childTypeSize := mem.r.targetData.TypeAllocSize(childType)
915 fields := make([]llvm.Value, numElements)
916 for i := range fields {
917 offset := i * int(childTypeSize)
918 field := rawValue{
919 buf: v.buf[offset:],
920 }
921 var err error
922 fields[i], err = field.toLLVMValue(childType, mem)
923 if err != nil {
924 return llvm.Value{}, err
925 }
926 if checks && fields[i].Type() != childType {
927 panic("child type doesn't match")
928 }
929 }
930 return llvm.ConstArray(childType, fields), nil
931 case llvm.PointerTypeKind:
932 if v.buf[0] > 255 {
933 // This is a regular pointer.
934 llvmValue, err := pointerValue{v.buf[0]}.toLLVMValue(llvm.Type{}, mem)
935 if err != nil {
936 return llvm.Value{}, err
937 }
938 if llvmValue.Type() != llvmType {
939 if llvmValue.Type().PointerAddressSpace() != llvmType.PointerAddressSpace() {
940 // Special case for AVR function pointers.
941 // Because go-llvm doesn't have addrspacecast at the moment,
942 // do it indirectly with a ptrtoint/inttoptr pair.
943 llvmValue = llvm.ConstIntToPtr(llvm.ConstPtrToInt(llvmValue, mem.r.uintptrType), llvmType)
944 }
945 }
946 return llvmValue, nil
947 }
948 // This is either a null pointer or a raw pointer for memory-mapped I/O
949 // (such as 0xe000ed00).
950 ptr := rawValue{v.buf[:mem.r.pointerSize]}.Uint(mem.r)
951 if ptr == 0 {
952 // Null pointer.
953 return llvm.ConstNull(llvmType), nil
954 }
955 var ptrValue llvm.Value // the underlying int
956 switch mem.r.pointerSize {
957 case 8:
958 ptrValue = llvm.ConstInt(llvmType.Context().Int64Type(), ptr, false)
959 case 4:
960 ptrValue = llvm.ConstInt(llvmType.Context().Int32Type(), ptr, false)
961 case 2:
962 ptrValue = llvm.ConstInt(llvmType.Context().Int16Type(), ptr, false)
963 default:
964 return llvm.Value{}, errors.New("interp: unknown pointer size")
965 }
966 return llvm.ConstIntToPtr(ptrValue, llvmType), nil
967 case llvm.DoubleTypeKind:
968 b := rawValue{v.buf[:8]}.Uint(mem.r)
969 f := math.Float64frombits(b)
970 return llvm.ConstFloat(llvmType, f), nil
971 case llvm.FloatTypeKind:
972 b := uint32(rawValue{v.buf[:4]}.Uint(mem.r))
973 f := math.Float32frombits(b)
974 return llvm.ConstFloat(llvmType, float64(f)), nil
975 default:
976 return llvm.Value{}, errors.New("interp: todo: raw value to LLVM value: " + llvmType.String())
977 }
978 }
979 980 func (v *rawValue) set(llvmValue llvm.Value, r *runner) {
981 if llvmValue.IsNull() {
982 // A zero value is common so check that first.
983 return
984 }
985 if !llvmValue.IsAGlobalValue().IsNil() {
986 ptrSize := r.pointerSize
987 ptr, err := r.getValue(llvmValue).asPointer(r)
988 if err != nil {
989 panic(err)
990 }
991 for i := uint32(0); i < ptrSize; i++ {
992 v.buf[i] = ptr.pointer
993 }
994 } else if !llvmValue.IsAConstantExpr().IsNil() {
995 switch llvmValue.Opcode() {
996 case llvm.IntToPtr, llvm.PtrToInt, llvm.BitCast:
997 // All these instructions effectively just reinterprets the bits
998 // (like a bitcast) while no bits change and keeping the same
999 // length, so just read its contents.
1000 v.set(llvmValue.Operand(0), r)
1001 case llvm.GetElementPtr:
1002 ptr := llvmValue.Operand(0)
1003 index := llvmValue.Operand(1)
1004 numOperands := llvmValue.OperandsCount()
1005 elementType := llvmValue.GEPSourceElementType()
1006 totalOffset := r.targetData.TypeAllocSize(elementType) * index.ZExtValue()
1007 for i := 2; i < numOperands; i++ {
1008 indexValue := llvmValue.Operand(i)
1009 if checks && indexValue.IsAConstantInt().IsNil() {
1010 panic("expected const gep index to be a constant integer")
1011 }
1012 index := indexValue.ZExtValue()
1013 switch elementType.TypeKind() {
1014 case llvm.StructTypeKind:
1015 // Indexing into a struct field.
1016 offsetInBytes := r.targetData.ElementOffset(elementType, int(index))
1017 totalOffset += offsetInBytes
1018 elementType = elementType.StructElementTypes()[index]
1019 default:
1020 // Indexing into an array.
1021 elementType = elementType.ElementType()
1022 elementSize := r.targetData.TypeAllocSize(elementType)
1023 totalOffset += index * elementSize
1024 }
1025 }
1026 ptrSize := r.pointerSize
1027 ptrValue, err := r.getValue(ptr).asPointer(r)
1028 if err != nil {
1029 panic(err)
1030 }
1031 ptrValue.pointer += totalOffset
1032 for i := uint32(0); i < ptrSize; i++ {
1033 v.buf[i] = ptrValue.pointer
1034 }
1035 case llvm.ICmp:
1036 // Note: constant icmp isn't supported anymore in LLVM 19.
1037 // Once we drop support for LLVM 18, this can be removed.
1038 size := r.targetData.TypeAllocSize(llvmValue.Operand(0).Type())
1039 lhs := newRawValue(uint32(size))
1040 rhs := newRawValue(uint32(size))
1041 lhs.set(llvmValue.Operand(0), r)
1042 rhs.set(llvmValue.Operand(1), r)
1043 if r.interpretICmp(lhs, rhs, llvmValue.IntPredicate()) {
1044 v.buf[0] = 1 // result is true
1045 } else {
1046 v.buf[0] = 0 // result is false
1047 }
1048 default:
1049 llvmValue.Dump()
1050 println()
1051 panic("unknown constant expr")
1052 }
1053 } else if llvmValue.IsUndef() {
1054 // Let undef be zero, by lack of an explicit 'undef' marker.
1055 } else {
1056 if checks && llvmValue.IsAConstant().IsNil() {
1057 panic("expected a constant")
1058 }
1059 llvmType := llvmValue.Type()
1060 switch llvmType.TypeKind() {
1061 case llvm.IntegerTypeKind:
1062 n := llvmValue.ZExtValue()
1063 switch llvmValue.Type().IntTypeWidth() {
1064 case 64:
1065 var buf [8]byte
1066 r.byteOrder.PutUint64(buf[:], n)
1067 for i, b := range buf {
1068 v.buf[i] = uint64(b)
1069 }
1070 case 32:
1071 var buf [4]byte
1072 r.byteOrder.PutUint32(buf[:], uint32(n))
1073 for i, b := range buf {
1074 v.buf[i] = uint64(b)
1075 }
1076 case 16:
1077 var buf [2]byte
1078 r.byteOrder.PutUint16(buf[:], uint16(n))
1079 for i, b := range buf {
1080 v.buf[i] = uint64(b)
1081 }
1082 case 8, 1:
1083 v.buf[0] = n
1084 default:
1085 panic("unknown integer size")
1086 }
1087 case llvm.StructTypeKind:
1088 numElements := llvmType.StructElementTypesCount()
1089 for i := 0; i < numElements; i++ {
1090 offset := r.targetData.ElementOffset(llvmType, i)
1091 field := rawValue{
1092 buf: v.buf[offset:],
1093 }
1094 field.set(r.builder.CreateExtractValue(llvmValue, i, ""), r)
1095 }
1096 case llvm.ArrayTypeKind:
1097 numElements := llvmType.ArrayLength()
1098 childType := llvmType.ElementType()
1099 childTypeSize := r.targetData.TypeAllocSize(childType)
1100 for i := 0; i < numElements; i++ {
1101 offset := i * int(childTypeSize)
1102 field := rawValue{
1103 buf: v.buf[offset:],
1104 }
1105 field.set(r.builder.CreateExtractValue(llvmValue, i, ""), r)
1106 }
1107 case llvm.DoubleTypeKind:
1108 f, _ := llvmValue.DoubleValue()
1109 var buf [8]byte
1110 r.byteOrder.PutUint64(buf[:], math.Float64bits(f))
1111 for i, b := range buf {
1112 v.buf[i] = uint64(b)
1113 }
1114 case llvm.FloatTypeKind:
1115 f, _ := llvmValue.DoubleValue()
1116 var buf [4]byte
1117 r.byteOrder.PutUint32(buf[:], math.Float32bits(float32(f)))
1118 for i, b := range buf {
1119 v.buf[i] = uint64(b)
1120 }
1121 default:
1122 llvmValue.Dump()
1123 println()
1124 panic("unknown constant")
1125 }
1126 }
1127 }
1128 1129 // hasPointer returns true if this raw value contains a pointer somewhere in the
1130 // buffer.
1131 func (v rawValue) hasPointer() bool {
1132 for _, p := range v.buf {
1133 if p > 255 {
1134 return true
1135 }
1136 }
1137 return false
1138 }
1139 1140 // localValue is a special implementation of the value interface. It is a
1141 // placeholder for other values in instruction operands, and is replaced with
1142 // one of the others before executing.
1143 type localValue struct {
1144 value llvm.Value
1145 }
1146 1147 func (v localValue) len(r *runner) uint32 {
1148 panic("interp: localValue.len")
1149 }
1150 1151 func (v localValue) String() string {
1152 return "<!>"
1153 }
1154 1155 func (v localValue) clone() value {
1156 panic("interp: localValue.clone()")
1157 }
1158 1159 func (v localValue) asPointer(r *runner) (pointerValue, error) {
1160 return pointerValue{}, errors.New("interp: localValue.asPointer called")
1161 }
1162 1163 func (v localValue) asRawValue(r *runner) rawValue {
1164 panic("interp: localValue.asRawValue")
1165 }
1166 1167 func (v localValue) Uint(r *runner) uint64 {
1168 panic("interp: localValue.Uint")
1169 }
1170 1171 func (v localValue) Int(r *runner) int64 {
1172 panic("interp: localValue.Int")
1173 }
1174 1175 func (v localValue) toLLVMValue(llvmType llvm.Type, mem *memoryView) (llvm.Value, error) {
1176 return v.value, nil
1177 }
1178 1179 func (r *runner) getValue(llvmValue llvm.Value) value {
1180 if checks && llvmValue.IsNil() {
1181 panic("nil llvmValue")
1182 }
1183 if !llvmValue.IsAGlobalValue().IsNil() {
1184 index, ok := r.globals[llvmValue]
1185 if !ok {
1186 obj := object{
1187 llvmGlobal: llvmValue,
1188 }
1189 index = len(r.objects)
1190 r.globals[llvmValue] = index
1191 r.objects = append(r.objects, obj)
1192 if !llvmValue.IsAGlobalVariable().IsNil() {
1193 obj.size = uint32(r.targetData.TypeAllocSize(llvmValue.GlobalValueType()))
1194 if initializer := llvmValue.Initializer(); !initializer.IsNil() {
1195 obj.buffer = r.getValue(initializer)
1196 obj.constant = llvmValue.IsGlobalConstant()
1197 }
1198 } else if !llvmValue.IsAFunction().IsNil() {
1199 // OK
1200 } else {
1201 panic("interp: unknown global value")
1202 }
1203 // Update the object after it has been created. This avoids an
1204 // infinite recursion when using getValue on a global that contains
1205 // a reference to itself.
1206 r.objects[index] = obj
1207 }
1208 return newPointerValue(r, index, 0)
1209 } else if !llvmValue.IsAConstant().IsNil() {
1210 if !llvmValue.IsAConstantInt().IsNil() {
1211 n := llvmValue.ZExtValue()
1212 switch llvmValue.Type().IntTypeWidth() {
1213 case 64:
1214 return literalValue{n}
1215 case 32:
1216 return literalValue{uint32(n)}
1217 case 16:
1218 return literalValue{uint16(n)}
1219 case 8, 1:
1220 return literalValue{uint8(n)}
1221 default:
1222 panic("unknown integer size")
1223 }
1224 }
1225 size := r.targetData.TypeAllocSize(llvmValue.Type())
1226 v := newRawValue(uint32(size))
1227 v.set(llvmValue, r)
1228 return v
1229 } else if !llvmValue.IsAInstruction().IsNil() || !llvmValue.IsAArgument().IsNil() {
1230 return localValue{llvmValue}
1231 } else if !llvmValue.IsAInlineAsm().IsNil() {
1232 return localValue{llvmValue}
1233 } else {
1234 llvmValue.Dump()
1235 println()
1236 panic("unknown value")
1237 }
1238 }
1239 1240 // readObjectLayout reads the object layout as it is stored by the compiler. It
1241 // returns the size in the number of words and the bitmap.
1242 //
1243 // For details on this format, see src/runtime/gc_precise.go.
1244 func (r *runner) readObjectLayout(layoutValue value) (uint64, *big.Int) {
1245 pointerSize := layoutValue.len(r)
1246 if checks && uint64(pointerSize) != r.targetData.TypeAllocSize(r.dataPtrType) {
1247 panic("inconsistent pointer size")
1248 }
1249 1250 // The object layout can be stored in a global variable, directly as an
1251 // integer value, or can be nil.
1252 ptr, err := layoutValue.asPointer(r)
1253 if err == errIntegerAsPointer {
1254 // It's an integer, which means it's a small object or unknown.
1255 layout := layoutValue.Uint(r)
1256 if layout == 0 {
1257 // Nil pointer, which means the layout is unknown.
1258 return 0, nil
1259 }
1260 if layout%2 != 1 {
1261 // Sanity check: the least significant bit must be set. This is how
1262 // the runtime can separate pointers from integers.
1263 panic("unexpected layout")
1264 }
1265 1266 // Determine format of bitfields in the integer.
1267 pointerBits := uint64(pointerSize * 8)
1268 var sizeFieldBits uint64
1269 switch pointerBits {
1270 case 16:
1271 sizeFieldBits = 4
1272 case 32:
1273 sizeFieldBits = 5
1274 case 64:
1275 sizeFieldBits = 6
1276 default:
1277 panic("unknown pointer size")
1278 }
1279 1280 // Extract fields.
1281 objectSizeWords := (layout >> 1) & (1<<sizeFieldBits - 1)
1282 bitmap := new(big.Int).SetUint64(layout >> (1 + sizeFieldBits))
1283 return objectSizeWords, bitmap
1284 }
1285 1286 // Read the object size in words and the bitmap from the global.
1287 buf := r.objects[ptr.index()].buffer.(rawValue)
1288 objectSizeWords := rawValue{buf: buf.buf[:r.pointerSize]}.Uint(r)
1289 rawByteValues := buf.buf[r.pointerSize:]
1290 rawBytes := make([]byte, len(rawByteValues))
1291 for i, v := range rawByteValues {
1292 if uint64(byte(v)) != v {
1293 panic("found pointer in data array?") // sanity check
1294 }
1295 rawBytes[i] = byte(v)
1296 }
1297 reverseBytes(rawBytes) // little-endian to big-endian
1298 bitmap := new(big.Int).SetBytes(rawBytes)
1299 return objectSizeWords, bitmap
1300 }
1301 1302 // getLLVMTypeFromLayout returns the 'layout type', which is an approximation of
1303 // the real type. Pointers are in the correct location but the actual object may
1304 // have some additional repetition, for example in the buffer of a slice.
1305 func (r *runner) getLLVMTypeFromLayout(layoutValue value) llvm.Type {
1306 objectSizeWords, bitmap := r.readObjectLayout(layoutValue)
1307 if bitmap == nil {
1308 // No information available.
1309 return llvm.Type{}
1310 }
1311 1312 if bitmap.BitLen() == 0 {
1313 // There are no pointers in this object, so treat this as a raw byte
1314 // buffer. This is important because objects without pointers may have
1315 // lower alignment.
1316 return r.mod.Context().Int8Type()
1317 }
1318 1319 // Create the LLVM type.
1320 pointerSize := layoutValue.len(r)
1321 pointerAlignment := r.targetData.PrefTypeAlignment(r.dataPtrType)
1322 var fields []llvm.Type
1323 for i := 0; i < int(objectSizeWords); {
1324 if bitmap.Bit(i) != 0 {
1325 // Pointer field.
1326 fields = append(fields, r.dataPtrType)
1327 i += int(pointerSize / uint32(pointerAlignment))
1328 } else {
1329 // Byte/word field.
1330 fields = append(fields, r.mod.Context().IntType(pointerAlignment*8))
1331 i += 1
1332 }
1333 }
1334 var llvmLayoutType llvm.Type
1335 if len(fields) == 1 {
1336 llvmLayoutType = fields[0]
1337 } else {
1338 llvmLayoutType = r.mod.Context().StructType(fields, false)
1339 }
1340 1341 objectSizeBytes := objectSizeWords * uint64(pointerAlignment)
1342 if checks && r.targetData.TypeAllocSize(llvmLayoutType) != objectSizeBytes {
1343 panic("unexpected size") // sanity check
1344 }
1345 return llvmLayoutType
1346 }
1347 1348 // Reverse a slice of bytes. From the wiki:
1349 // https://github.com/golang/go/wiki/SliceTricks#reversing
1350 func reverseBytes(buf []byte) {
1351 for i := len(buf)/2 - 1; i >= 0; i-- {
1352 opp := len(buf) - 1 - i
1353 buf[i], buf[opp] = buf[opp], buf[i]
1354 }
1355 }
1356