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