diff options
Diffstat (limited to 'vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/engine_cache.go')
-rw-r--r-- | vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/engine_cache.go | 303 |
1 files changed, 0 insertions, 303 deletions
diff --git a/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/engine_cache.go b/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/engine_cache.go deleted file mode 100644 index e49353dc8..000000000 --- a/vendor/github.com/tetratelabs/wazero/internal/engine/wazevo/engine_cache.go +++ /dev/null @@ -1,303 +0,0 @@ -package wazevo - -import ( - "bytes" - "context" - "crypto/sha256" - "encoding/binary" - "fmt" - "hash/crc32" - "io" - "runtime" - "unsafe" - - "github.com/tetratelabs/wazero/experimental" - "github.com/tetratelabs/wazero/internal/engine/wazevo/backend" - "github.com/tetratelabs/wazero/internal/engine/wazevo/ssa" - "github.com/tetratelabs/wazero/internal/engine/wazevo/wazevoapi" - "github.com/tetratelabs/wazero/internal/filecache" - "github.com/tetratelabs/wazero/internal/platform" - "github.com/tetratelabs/wazero/internal/u32" - "github.com/tetratelabs/wazero/internal/u64" - "github.com/tetratelabs/wazero/internal/wasm" -) - -var crc = crc32.MakeTable(crc32.Castagnoli) - -// fileCacheKey returns a key for the file cache. -// In order to avoid collisions with the existing compiler, we do not use m.ID directly, -// but instead we rehash it with magic. -func fileCacheKey(m *wasm.Module) (ret filecache.Key) { - s := sha256.New() - s.Write(m.ID[:]) - s.Write(magic) - // Write the CPU features so that we can cache the compiled module for the same CPU. - // This prevents the incompatible CPU features from being used. - cpu := platform.CpuFeatures.Raw() - // Reuse the `ret` buffer to write the first 8 bytes of the CPU features so that we can avoid the allocation. - binary.LittleEndian.PutUint64(ret[:8], cpu) - s.Write(ret[:8]) - // Finally, write the hash to the ret buffer. - s.Sum(ret[:0]) - return -} - -func (e *engine) addCompiledModule(module *wasm.Module, cm *compiledModule) (err error) { - e.addCompiledModuleToMemory(module, cm) - if !module.IsHostModule && e.fileCache != nil { - err = e.addCompiledModuleToCache(module, cm) - } - return -} - -func (e *engine) getCompiledModule(module *wasm.Module, listeners []experimental.FunctionListener, ensureTermination bool) (cm *compiledModule, ok bool, err error) { - cm, ok = e.getCompiledModuleFromMemory(module) - if ok { - return - } - cm, ok, err = e.getCompiledModuleFromCache(module) - if ok { - cm.parent = e - cm.module = module - cm.sharedFunctions = e.sharedFunctions - cm.ensureTermination = ensureTermination - cm.offsets = wazevoapi.NewModuleContextOffsetData(module, len(listeners) > 0) - if len(listeners) > 0 { - cm.listeners = listeners - cm.listenerBeforeTrampolines = make([]*byte, len(module.TypeSection)) - cm.listenerAfterTrampolines = make([]*byte, len(module.TypeSection)) - for i := range module.TypeSection { - typ := &module.TypeSection[i] - before, after := e.getListenerTrampolineForType(typ) - cm.listenerBeforeTrampolines[i] = before - cm.listenerAfterTrampolines[i] = after - } - } - e.addCompiledModuleToMemory(module, cm) - ssaBuilder := ssa.NewBuilder() - machine := newMachine() - be := backend.NewCompiler(context.Background(), machine, ssaBuilder) - cm.executables.compileEntryPreambles(module, machine, be) - - // Set the finalizer. - e.setFinalizer(cm.executables, executablesFinalizer) - } - return -} - -func (e *engine) addCompiledModuleToMemory(m *wasm.Module, cm *compiledModule) { - e.mux.Lock() - defer e.mux.Unlock() - e.compiledModules[m.ID] = cm - if len(cm.executable) > 0 { - e.addCompiledModuleToSortedList(cm) - } -} - -func (e *engine) getCompiledModuleFromMemory(module *wasm.Module) (cm *compiledModule, ok bool) { - e.mux.RLock() - defer e.mux.RUnlock() - cm, ok = e.compiledModules[module.ID] - return -} - -func (e *engine) addCompiledModuleToCache(module *wasm.Module, cm *compiledModule) (err error) { - if e.fileCache == nil || module.IsHostModule { - return - } - err = e.fileCache.Add(fileCacheKey(module), serializeCompiledModule(e.wazeroVersion, cm)) - return -} - -func (e *engine) getCompiledModuleFromCache(module *wasm.Module) (cm *compiledModule, hit bool, err error) { - if e.fileCache == nil || module.IsHostModule { - return - } - - // Check if the entries exist in the external cache. - var cached io.ReadCloser - cached, hit, err = e.fileCache.Get(fileCacheKey(module)) - if !hit || err != nil { - return - } - - // Otherwise, we hit the cache on external cache. - // We retrieve *code structures from `cached`. - var staleCache bool - // Note: cached.Close is ensured to be called in deserializeCodes. - cm, staleCache, err = deserializeCompiledModule(e.wazeroVersion, cached) - if err != nil { - hit = false - return - } else if staleCache { - return nil, false, e.fileCache.Delete(fileCacheKey(module)) - } - return -} - -var magic = []byte{'W', 'A', 'Z', 'E', 'V', 'O'} - -func serializeCompiledModule(wazeroVersion string, cm *compiledModule) io.Reader { - buf := bytes.NewBuffer(nil) - // First 6 byte: WAZEVO header. - buf.Write(magic) - // Next 1 byte: length of version: - buf.WriteByte(byte(len(wazeroVersion))) - // Version of wazero. - buf.WriteString(wazeroVersion) - // Number of *code (== locally defined functions in the module): 4 bytes. - buf.Write(u32.LeBytes(uint32(len(cm.functionOffsets)))) - for _, offset := range cm.functionOffsets { - // The offset of this function in the executable (8 bytes). - buf.Write(u64.LeBytes(uint64(offset))) - } - // The length of code segment (8 bytes). - buf.Write(u64.LeBytes(uint64(len(cm.executable)))) - // Append the native code. - buf.Write(cm.executable) - // Append checksum. - checksum := crc32.Checksum(cm.executable, crc) - buf.Write(u32.LeBytes(checksum)) - if sm := cm.sourceMap; len(sm.executableOffsets) > 0 { - buf.WriteByte(1) // indicates that source map is present. - l := len(sm.wasmBinaryOffsets) - buf.Write(u64.LeBytes(uint64(l))) - executableAddr := uintptr(unsafe.Pointer(&cm.executable[0])) - for i := 0; i < l; i++ { - buf.Write(u64.LeBytes(sm.wasmBinaryOffsets[i])) - // executableOffsets is absolute address, so we need to subtract executableAddr. - buf.Write(u64.LeBytes(uint64(sm.executableOffsets[i] - executableAddr))) - } - } else { - buf.WriteByte(0) // indicates that source map is not present. - } - return bytes.NewReader(buf.Bytes()) -} - -func deserializeCompiledModule(wazeroVersion string, reader io.ReadCloser) (cm *compiledModule, staleCache bool, err error) { - defer reader.Close() - cacheHeaderSize := len(magic) + 1 /* version size */ + len(wazeroVersion) + 4 /* number of functions */ - - // Read the header before the native code. - header := make([]byte, cacheHeaderSize) - n, err := reader.Read(header) - if err != nil { - return nil, false, fmt.Errorf("compilationcache: error reading header: %v", err) - } - - if n != cacheHeaderSize { - return nil, false, fmt.Errorf("compilationcache: invalid header length: %d", n) - } - - if !bytes.Equal(header[:len(magic)], magic) { - return nil, false, fmt.Errorf( - "compilationcache: invalid magic number: got %s but want %s", magic, header[:len(magic)]) - } - - // Check the version compatibility. - versionSize := int(header[len(magic)]) - - cachedVersionBegin, cachedVersionEnd := len(magic)+1, len(magic)+1+versionSize - if cachedVersionEnd >= len(header) { - staleCache = true - return - } else if cachedVersion := string(header[cachedVersionBegin:cachedVersionEnd]); cachedVersion != wazeroVersion { - staleCache = true - return - } - - functionsNum := binary.LittleEndian.Uint32(header[len(header)-4:]) - cm = &compiledModule{functionOffsets: make([]int, functionsNum), executables: &executables{}} - - var eightBytes [8]byte - for i := uint32(0); i < functionsNum; i++ { - // Read the offset of each function in the executable. - var offset uint64 - if offset, err = readUint64(reader, &eightBytes); err != nil { - err = fmt.Errorf("compilationcache: error reading func[%d] executable offset: %v", i, err) - return - } - cm.functionOffsets[i] = int(offset) - } - - executableLen, err := readUint64(reader, &eightBytes) - if err != nil { - err = fmt.Errorf("compilationcache: error reading executable size: %v", err) - return - } - - if executableLen > 0 { - executable, err := platform.MmapCodeSegment(int(executableLen)) - if err != nil { - err = fmt.Errorf("compilationcache: error mmapping executable (len=%d): %v", executableLen, err) - return nil, false, err - } - - _, err = io.ReadFull(reader, executable) - if err != nil { - err = fmt.Errorf("compilationcache: error reading executable (len=%d): %v", executableLen, err) - return nil, false, err - } - - expected := crc32.Checksum(executable, crc) - if _, err = io.ReadFull(reader, eightBytes[:4]); err != nil { - return nil, false, fmt.Errorf("compilationcache: could not read checksum: %v", err) - } else if checksum := binary.LittleEndian.Uint32(eightBytes[:4]); expected != checksum { - return nil, false, fmt.Errorf("compilationcache: checksum mismatch (expected %d, got %d)", expected, checksum) - } - - if runtime.GOARCH == "arm64" { - // On arm64, we cannot give all of rwx at the same time, so we change it to exec. - if err = platform.MprotectRX(executable); err != nil { - return nil, false, err - } - } - cm.executable = executable - } - - if _, err := io.ReadFull(reader, eightBytes[:1]); err != nil { - return nil, false, fmt.Errorf("compilationcache: error reading source map presence: %v", err) - } - - if eightBytes[0] == 1 { - sm := &cm.sourceMap - sourceMapLen, err := readUint64(reader, &eightBytes) - if err != nil { - err = fmt.Errorf("compilationcache: error reading source map length: %v", err) - return nil, false, err - } - executableOffset := uintptr(unsafe.Pointer(&cm.executable[0])) - for i := uint64(0); i < sourceMapLen; i++ { - wasmBinaryOffset, err := readUint64(reader, &eightBytes) - if err != nil { - err = fmt.Errorf("compilationcache: error reading source map[%d] wasm binary offset: %v", i, err) - return nil, false, err - } - executableRelativeOffset, err := readUint64(reader, &eightBytes) - if err != nil { - err = fmt.Errorf("compilationcache: error reading source map[%d] executable offset: %v", i, err) - return nil, false, err - } - sm.wasmBinaryOffsets = append(sm.wasmBinaryOffsets, wasmBinaryOffset) - // executableOffsets is absolute address, so we need to add executableOffset. - sm.executableOffsets = append(sm.executableOffsets, uintptr(executableRelativeOffset)+executableOffset) - } - } - return -} - -// readUint64 strictly reads an uint64 in little-endian byte order, using the -// given array as a buffer. This returns io.EOF if less than 8 bytes were read. -func readUint64(reader io.Reader, b *[8]byte) (uint64, error) { - s := b[0:8] - n, err := reader.Read(s) - if err != nil { - return 0, err - } else if n < 8 { // more strict than reader.Read - return 0, io.EOF - } - - // Read the u64 from the underlying buffer. - ret := binary.LittleEndian.Uint64(s) - return ret, nil -} |