spawnbinary.go raw

   1  package compiler
   2  
   3  // Cross-binary spawn: when a spawn target's package was loaded from a .mxh
   4  // cache file (loader.MXHPackages), the compiler routes to SpawnBinary instead
   5  // of spawnDomain. The binary path and .mxh hash are embedded as string
   6  // constants resolved from the mxinstall cache at compile time.
   7  
   8  import (
   9  	"strings"
  10  
  11  	"moxie/cache"
  12  	"moxie/loader"
  13  	"golang.org/x/tools/go/ssa"
  14  	"tinygo.org/x/go-llvm"
  15  )
  16  
  17  // isExternalMXHSpawn returns true if the spawn target function's package was
  18  // loaded from a .mxh cache file and should use the cross-binary spawn path.
  19  func (b *builder) isExternalMXHSpawn(fn *ssa.Function) bool {
  20  	if fn.Package() == nil {
  21  		return false
  22  	}
  23  	return b.MXHPackages[fn.Package().Pkg.Path()]
  24  }
  25  
  26  // createCrossBinarySpawn handles spawn() calls whose target is in a .mxh
  27  // package. It emits a call to runtime.SpawnBinary with the cached binary path
  28  // and the .mxh content hash for the protocol types crossing the boundary.
  29  //
  30  // The child binary is a standalone program that reads MOXIE_IPC_FD from its
  31  // environment to find the IPC channel established by SpawnBinary. The .mxh
  32  // hash is exchanged during the handshake to verify protocol compatibility.
  33  func (b *builder) createCrossBinarySpawn(targetFn *ssa.Function, instr *ssa.CallCommon) (llvm.Value, error) {
  34  	importPath := targetFn.Package().Pkg.Path()
  35  
  36  	// Resolve binary path from cache.
  37  	binPath := cache.BinPath(b.Triple, importPath, binaryBaseName(importPath))
  38  
  39  	// Look up the .mxh hash for this package.
  40  	hash := loader.MXHHashForImport(importPath)
  41  	if hash == "" {
  42  		b.addError(instr.Pos(), "spawn: no .mxh found for "+importPath+"; run `moxie install "+importPath+"`")
  43  		return llvm.Value{}, nil
  44  	}
  45  
  46  	// Build LLVM string constants for binPath and hash.
  47  	binPathVal := b.makeStringConst(binPath)
  48  	hashVal := b.makeStringConst(hash)
  49  
  50  	// Call runtime.SpawnBinary(binPath, hash).
  51  	runtimePkg := b.program.ImportedPackage("runtime")
  52  	spawnBinaryMember, ok := runtimePkg.Members["SpawnBinary"]
  53  	if !ok {
  54  		b.addError(instr.Pos(), "spawn: runtime.SpawnBinary not found — rebuild runtime")
  55  		return llvm.Value{}, nil
  56  	}
  57  	fnType, fnVal := b.getFunction(spawnBinaryMember.(*ssa.Function))
  58  	b.createCall(fnType, fnVal, []llvm.Value{binPathVal, hashVal, llvm.Undef(b.dataPtrType)}, "ipc.fd")
  59  
  60  	// Cross-binary spawn doesn't return a lifecycle channel yet.
  61  	// TODO: extend SpawnBinary to register domain and return lifecycle chan.
  62  	return llvm.Undef(b.dataPtrType), nil
  63  }
  64  
  65  // binaryBaseName extracts the binary name from an import path.
  66  // "git.smesh.lol/iskradb" → "iskradb"
  67  func binaryBaseName(importPath string) string {
  68  	if i := strings.LastIndexByte(importPath, '/'); i >= 0 {
  69  		return importPath[i+1:]
  70  	}
  71  	return importPath
  72  }
  73  
  74  // makeStringConst creates an LLVM string constant (_string struct) for s.
  75  // Follows the same pattern as constant string emission in compiler.go:3155.
  76  func (b *builder) makeStringConst(s string) llvm.Value {
  77  	strLen := llvm.ConstInt(b.uintptrType, uint64(len(s)), false)
  78  	var strPtr llvm.Value
  79  	if s != "" {
  80  		globalType := llvm.ArrayType(b.ctx.Int8Type(), len(s))
  81  		global := llvm.AddGlobal(b.mod, globalType, ".spawn.str")
  82  		global.SetInitializer(b.ctx.ConstString(s, false))
  83  		global.SetLinkage(llvm.InternalLinkage)
  84  		global.SetGlobalConstant(true)
  85  		global.SetUnnamedAddr(true)
  86  		global.SetAlignment(1)
  87  		zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
  88  		strPtr = llvm.ConstInBoundsGEP(globalType, global, []llvm.Value{zero, zero})
  89  	} else {
  90  		strPtr = llvm.ConstNull(b.dataPtrType)
  91  	}
  92  	return llvm.ConstNamedStruct(b.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen, strLen})
  93  }
  94