compiler.go raw

   1  package compiler
   2  
   3  import (
   4  	"debug/dwarf"
   5  	"errors"
   6  	"fmt"
   7  	"os"
   8  	"go/ast"
   9  	"go/constant"
  10  	"go/token"
  11  	"go/types"
  12  	"math/bits"
  13  	"path"
  14  	"path/filepath"
  15  	"regexp"
  16  	"sort"
  17  	"strconv"
  18  	"strings"
  19  
  20  	"moxie/compiler/llvmutil"
  21  	"moxie/loader"
  22  	"moxie/src/moxie"
  23  	"golang.org/x/tools/go/ssa"
  24  	"golang.org/x/tools/go/types/typeutil"
  25  	"tinygo.org/x/go-llvm"
  26  )
  27  
  28  func init() {
  29  	llvm.InitializeAllTargets()
  30  	llvm.InitializeAllTargetMCs()
  31  	llvm.InitializeAllTargetInfos()
  32  	llvm.InitializeAllAsmParsers()
  33  	llvm.InitializeAllAsmPrinters()
  34  }
  35  
  36  // Config is the configuration for the compiler. Most settings should be copied
  37  // directly from compileopts.Config, it recreated here to decouple the compiler
  38  // package a bit and because it makes caching easier.
  39  //
  40  // This struct can be used for caching: if one of the flags here changes the
  41  // code must be recompiled.
  42  type Config struct {
  43  	// Target and output information.
  44  	Triple          string
  45  	CPU             string
  46  	Features        string
  47  	ABI             string
  48  	GOOS            string
  49  	GOARCH          string
  50  	BuildMode       string
  51  	CodeModel       string
  52  	RelocationModel string
  53  	SizeLevel       int
  54  	MoxieVersion    string // for llvm.ident
  55  
  56  	// MXHPackages is the set of import paths loaded from .mxh cache files.
  57  	// Used to detect cross-binary spawn targets.
  58  	MXHPackages map[string]bool
  59  
  60  	// Various compiler options that determine how code is generated.
  61  	Scheduler          string
  62  	AutomaticStackSize bool
  63  	DefaultStackSize   uint64
  64  	MaxStackAlloc      uint64
  65  	Debug              bool // Whether to emit debug information in the LLVM module.
  66  	Nobounds           bool // Whether to skip bounds checks
  67  	PanicStrategy      string
  68  	StandaloneRuntime  bool // keep symbols external for cross-module linking
  69  }
  70  
  71  // compilerContext contains function-independent data that should still be
  72  // available while compiling every function. It is not strictly read-only, but
  73  // must not contain function-dependent data such as an IR builder.
  74  type compilerContext struct {
  75  	*Config
  76  	DumpSSA     bool
  77  	PrintAllocs *regexp.Regexp // set when -print-allocs active; kept off Config to avoid cache key churn
  78  	mod              llvm.Module
  79  	ctx              llvm.Context
  80  	builder          llvm.Builder // only used for constant operations
  81  	dibuilder        *llvm.DIBuilder
  82  	cu               llvm.Metadata
  83  	difiles          map[string]llvm.Metadata
  84  	ditypes          map[types.Type]llvm.Metadata
  85  	llvmTypes        typeutil.Map
  86  	interfaceTypes   typeutil.Map
  87  	machine          llvm.TargetMachine
  88  	targetData       llvm.TargetData
  89  	intType          llvm.Type
  90  	dataPtrType      llvm.Type // pointer in address space 0
  91  	funcPtrType      llvm.Type // pointer in function address space (1 for AVR, 0 elsewhere)
  92  	funcPtrAddrSpace int
  93  	uintptrType      llvm.Type
  94  	program          *ssa.Program
  95  	diagnostics      []error
  96  	functionInfos    map[*ssa.Function]functionInfo
  97  	astComments      map[string]*ast.CommentGroup
  98  	embedGlobals     map[string][]*loader.EmbedFile
  99  	pkg              *types.Package
 100  	packageDir       string // directory for this package
 101  	runtimePkg       *types.Package
 102  
 103  	// Loader-generated make() byte offsets per file. Used by the restriction
 104  	// check to distinguish literal-syntax lowerings from user-written make().
 105  	makeExemptOffsets map[string][]int
 106  
 107  
 108  	// Wasm spawn dispatch table. Populated by scanWasmSpawnTargets before
 109  	// package compilation; consumed by emitWasmSpawnEntry after.
 110  	wasmSpawnTargets []*wasmSpawnTarget
 111  	wasmSpawnIndex   map[*ssa.Function]int32
 112  
 113  	// Native spawn target table. Populated by scanNativeSpawnTargets;
 114  	// consumed by setupNativeSpawnTargetChannels at child-target entry
 115  	// to mark channel params pipe-bound to ChildPipeFd.
 116  	nativeSpawnTargets []*nativeSpawnTarget
 117  	nativeSpawnIndex   map[*ssa.Function]*nativeSpawnTarget
 118  }
 119  
 120  // newCompilerContext returns a new compiler context ready for use, most
 121  // importantly with a newly created LLVM context and module.
 122  func newCompilerContext(moduleName string, machine llvm.TargetMachine, config *Config, dumpSSA bool, printAllocs *regexp.Regexp) *compilerContext {
 123  	c := &compilerContext{
 124  		Config:        config,
 125  		DumpSSA:       dumpSSA,
 126  		PrintAllocs:   printAllocs,
 127  		difiles:       make(map[string]llvm.Metadata),
 128  		ditypes:       make(map[types.Type]llvm.Metadata),
 129  		machine:       machine,
 130  		targetData:    machine.CreateTargetData(),
 131  		functionInfos: map[*ssa.Function]functionInfo{},
 132  		astComments:   map[string]*ast.CommentGroup{},
 133  	}
 134  
 135  	c.ctx = llvm.NewContext()
 136  	c.builder = c.ctx.NewBuilder()
 137  	c.mod = c.ctx.NewModule(moduleName)
 138  	c.mod.SetTarget(config.Triple)
 139  	c.mod.SetDataLayout(c.targetData.String())
 140  	if c.Debug {
 141  		c.dibuilder = llvm.NewDIBuilder(c.mod)
 142  	}
 143  
 144  	c.uintptrType = c.ctx.IntType(c.targetData.PointerSize() * 8)
 145  	// Moxie: int is always 32 bits on all targets. Slice headers still use
 146  	// uintptr-sized len/cap; trunc/ext happens at the int boundary.
 147  	c.intType = c.ctx.Int32Type()
 148  	c.dataPtrType = llvm.PointerType(c.ctx.Int8Type(), 0)
 149  
 150  	dummyFuncType := llvm.FunctionType(c.ctx.VoidType(), nil, false)
 151  	dummyFunc := llvm.AddFunction(c.mod, "moxie.dummy", dummyFuncType)
 152  	c.funcPtrAddrSpace = dummyFunc.Type().PointerAddressSpace()
 153  	c.funcPtrType = dummyFunc.Type()
 154  	dummyFunc.EraseFromParentAsFunction()
 155  
 156  	return c
 157  }
 158  
 159  // Dispose everything related to the context, _except_ for the IR module (and
 160  // the associated context).
 161  func (c *compilerContext) dispose() {
 162  	c.builder.Dispose()
 163  }
 164  
 165  // builder contains all information relevant to build a single function.
 166  type builder struct {
 167  	*compilerContext
 168  	llvm.Builder
 169  	fn                *ssa.Function
 170  	llvmFnType        llvm.Type
 171  	llvmFn            llvm.Value
 172  	info              functionInfo
 173  	locals            map[ssa.Value]llvm.Value // local variables
 174  	blockInfo         []blockInfo
 175  	currentBlock      *ssa.BasicBlock
 176  	currentBlockInfo  *blockInfo
 177  	tarjanStack       []uint
 178  	tarjanIndex       uint
 179  	phis              []phiNode
 180  	deferPtr          llvm.Value
 181  	deferFrame        llvm.Value
 182  	landingpad        llvm.BasicBlock
 183  	difunc            llvm.Metadata
 184  	dilocals          map[*types.Var]llvm.Metadata
 185  	initInlinedAt     llvm.Metadata            // fake inlinedAt position
 186  	initPseudoFuncs   map[string]llvm.Metadata // fake "inlined" functions for proper init debug locations
 187  	allDeferFuncs     []interface{}
 188  	deferFuncs        map[*ssa.Function]int
 189  	deferInvokeFuncs  map[string]int
 190  	deferClosureFuncs map[*ssa.Function]int
 191  	deferExprFuncs    map[ssa.Value]int
 192  	selectRecvBuf     map[*ssa.Select]llvm.Value
 193  	deferBuiltinFuncs map[ssa.Value]deferBuiltin
 194  	runDefersBlock    []llvm.BasicBlock
 195  	afterDefersBlock  []llvm.BasicBlock
 196  
 197  	// sabChannels tracks SSA channel values that are SAB-backed (spawn boundary).
 198  	// Maps the SSA channel value to its int32 LLVM SAB handle value.
 199  	// Populated by createWasmSpawn (parent side) and setupWasmSpawnTargetChannels
 200  	// (child/spawn-target side).
 201  	sabChannels map[ssa.Value]llvm.Value
 202  
 203  	// pipeChannels tracks SSA channel values that route through a fork+
 204  	// socketpair pipe (native spawn boundary). Presence in the map means
 205  	// chan send/recv on this value should emit PipeChanSendCodec /
 206  	// PipeChanRecvCodec (Codec-based serialization) instead of the
 207  	// in-runtime queue path. Populated by createSpawn (parent) and
 208  	// setupNativeSpawnTargetChannels (child).
 209  	pipeChannels map[ssa.Value]bool
 210  
 211  	// Arena prologue/epilogue state.
 212  	arenaMarkVal  llvm.Value // legacy: saved arena offset (unused in per-fn model)
 213  	outerArenaVal llvm.Value // caller's arena (restored before return)
 214  	fnArenaVal    llvm.Value // per-function arena (freed on return)
 215  }
 216  
 217  func newBuilder(c *compilerContext, irbuilder llvm.Builder, f *ssa.Function) *builder {
 218  	fnType, fn := c.getFunction(f)
 219  	return &builder{
 220  		compilerContext: c,
 221  		Builder:         irbuilder,
 222  		fn:              f,
 223  		llvmFnType:      fnType,
 224  		llvmFn:          fn,
 225  		info:            c.getFunctionInfo(f),
 226  		locals:          make(map[ssa.Value]llvm.Value),
 227  		dilocals:        make(map[*types.Var]llvm.Metadata),
 228  	}
 229  }
 230  
 231  type blockInfo struct {
 232  	// entry is the LLVM basic block corresponding to the start of this *ssa.Block.
 233  	entry llvm.BasicBlock
 234  
 235  	// exit is the LLVM basic block corresponding to the end of this *ssa.Block.
 236  	// It will be different than entry if any of the block's instructions contain internal branches.
 237  	exit llvm.BasicBlock
 238  
 239  	// tarjan holds state for applying Tarjan's strongly connected components algorithm to the CFG.
 240  	// This is used by defer.go to determine whether to stack- or heap-allocate defer data.
 241  	tarjan tarjanNode
 242  }
 243  
 244  type deferBuiltin struct {
 245  	callName string
 246  	pos      token.Pos
 247  	argTypes []types.Type
 248  	callback int
 249  }
 250  
 251  type phiNode struct {
 252  	ssa  *ssa.Phi
 253  	llvm llvm.Value
 254  }
 255  
 256  // NewTargetMachine returns a new llvm.TargetMachine based on the passed-in
 257  // configuration. It is used by the compiler and is needed for machine code
 258  // emission.
 259  func NewTargetMachine(config *Config) (llvm.TargetMachine, error) {
 260  	target, err := llvm.GetTargetFromTriple(config.Triple)
 261  	if err != nil {
 262  		return llvm.TargetMachine{}, err
 263  	}
 264  
 265  	var codeModel llvm.CodeModel
 266  	var relocationModel llvm.RelocMode
 267  
 268  	switch config.CodeModel {
 269  	case "default":
 270  		codeModel = llvm.CodeModelDefault
 271  	case "tiny":
 272  		codeModel = llvm.CodeModelTiny
 273  	case "small":
 274  		codeModel = llvm.CodeModelSmall
 275  	case "kernel":
 276  		codeModel = llvm.CodeModelKernel
 277  	case "medium":
 278  		codeModel = llvm.CodeModelMedium
 279  	case "large":
 280  		codeModel = llvm.CodeModelLarge
 281  	}
 282  
 283  	switch config.RelocationModel {
 284  	case "static":
 285  		relocationModel = llvm.RelocStatic
 286  	case "pic":
 287  		relocationModel = llvm.RelocPIC
 288  	case "dynamicnopic":
 289  		relocationModel = llvm.RelocDynamicNoPic
 290  	}
 291  
 292  	machine := target.CreateTargetMachine(config.Triple, config.CPU, config.Features, llvm.CodeGenLevelDefault, relocationModel, codeModel)
 293  	return machine, nil
 294  }
 295  
 296  // Sizes returns a types.Sizes appropriate for the given target machine. It
 297  // includes the correct int size and alignment as is necessary for the Go
 298  // typechecker.
 299  func Sizes(machine llvm.TargetMachine) types.Sizes {
 300  	targetData := machine.CreateTargetData()
 301  	defer targetData.Dispose()
 302  
 303  	// Moxie: int is always 32 bits on all targets.
 304  	intWidth := 32
 305  
 306  	// Construct a complex128 type because that's likely the type with the
 307  	// biggest alignment on most/all ABIs.
 308  	ctx := llvm.NewContext()
 309  	defer ctx.Dispose()
 310  	complex128Type := ctx.StructType([]llvm.Type{ctx.DoubleType(), ctx.DoubleType()}, false)
 311  	return &stdSizes{
 312  		IntSize:  int64(intWidth / 8),
 313  		PtrSize:  int64(targetData.PointerSize()),
 314  		MaxAlign: int64(targetData.ABITypeAlignment(complex128Type)),
 315  	}
 316  }
 317  
 318  // CompilePackage compiles a single package to a LLVM module.
 319  func CompilePackage(moduleName string, pkg *loader.Package, ssaPkg *ssa.Package, machine llvm.TargetMachine, config *Config, dumpSSA bool, printAllocs *regexp.Regexp) (llvm.Module, []error) {
 320  	c := newCompilerContext(moduleName, machine, config, dumpSSA, printAllocs)
 321  	defer c.dispose()
 322  	c.packageDir = pkg.OriginalDir()
 323  	c.embedGlobals = pkg.EmbedGlobals
 324  	c.makeExemptOffsets = pkg.MakeExemptOffsets
 325  	c.pkg = pkg.Pkg
 326  	rtSSAPkg := ssaPkg.Prog.ImportedPackage("runtime")
 327  	if rtSSAPkg != nil {
 328  		c.runtimePkg = rtSSAPkg.Pkg
 329  	} else {
 330  		// Standalone runtime compilation: runtime isn't "imported" but IS
 331  		// in the program's package set.
 332  		for _, p := range ssaPkg.Prog.AllPackages() {
 333  			if p.Pkg != nil && p.Pkg.Path() == "runtime" {
 334  				c.runtimePkg = p.Pkg
 335  				rtSSAPkg = p
 336  				break
 337  			}
 338  		}
 339  		if c.runtimePkg == nil {
 340  			fmt.Fprintf(os.Stderr, "[debug] runtimePkg not found. compiling=%s allPkgs=%d\n", pkg.Pkg.Path(), len(ssaPkg.Prog.AllPackages()))
 341  			for _, p := range ssaPkg.Prog.AllPackages() {
 342  				if p.Pkg != nil {
 343  					fmt.Fprintf(os.Stderr, "  pkg: %s\n", p.Pkg.Path())
 344  				}
 345  			}
 346  		}
 347  	}
 348  	c.program = ssaPkg.Prog
 349  
 350  	// Convert AST to SSA.
 351  	ssaPkg.Build()
 352  
 353  	// Package-level Moxie restrictions (steps 8, 9, 10).
 354  	c.checkPackageRestrictions(pkg)
 355  	// API stability enforcement (step 11b).
 356  	c.checkAPIStability(pkg)
 357  
 358  	// Pre-scan for wasm spawn targets before any function compilation.
 359  	c.scanWasmSpawnTargets(ssaPkg)
 360  	// Pre-scan for native spawn targets so child-side prologue can wire ChildPipeFd.
 361  	c.scanNativeSpawnTargets(ssaPkg)
 362  
 363  	// Initialize debug information.
 364  	if c.Debug {
 365  		c.cu = c.dibuilder.CreateCompileUnit(llvm.DICompileUnit{
 366  			Language:  0xb, // DW_LANG_C99 (0xc, off-by-one?)
 367  			File:      "<unknown>",
 368  			Dir:       "",
 369  			Producer:  "Moxie",
 370  			Optimized: true,
 371  		})
 372  	}
 373  
 374  	// Load comments such as //go:extern on globals.
 375  	c.loadASTComments(pkg)
 376  
 377  	// Predeclare the runtime.alloc function, which is used by the wordpack
 378  	// functionality.
 379  	var allocPkg *ssa.Package
 380  	if rtSSAPkg != nil {
 381  		allocPkg = rtSSAPkg
 382  	} else if pkg.Pkg.Path() == "runtime" {
 383  		allocPkg = ssaPkg
 384  	}
 385  	if allocPkg != nil {
 386  		if allocMember, ok := allocPkg.Members["alloc"]; ok {
 387  			if allocFn, ok2 := allocMember.(*ssa.Function); ok2 {
 388  				c.getFunction(allocFn)
 389  			}
 390  		}
 391  	}
 392  
 393  	// Compile all functions, methods, and global variables in this package.
 394  	irbuilder := c.ctx.NewBuilder()
 395  	defer irbuilder.Dispose()
 396  	c.createPackage(irbuilder, ssaPkg)
 397  
 398  	// Emit __spawn_entry export for wasm target after all spawn targets compiled.
 399  	c.emitWasmSpawnEntry(irbuilder)
 400  
 401  	// see: https://reviews.llvm.org/D18355
 402  	if c.Debug {
 403  		c.mod.AddNamedMetadataOperand("llvm.module.flags",
 404  			c.ctx.MDNode([]llvm.Metadata{
 405  				llvm.ConstInt(c.ctx.Int32Type(), 2, false).ConstantAsMetadata(), // Warning on mismatch
 406  				c.ctx.MDString("Debug Info Version"),
 407  				llvm.ConstInt(c.ctx.Int32Type(), 3, false).ConstantAsMetadata(), // DWARF version
 408  			}),
 409  		)
 410  		c.mod.AddNamedMetadataOperand("llvm.module.flags",
 411  			c.ctx.MDNode([]llvm.Metadata{
 412  				llvm.ConstInt(c.ctx.Int32Type(), 7, false).ConstantAsMetadata(), // Max on mismatch
 413  				c.ctx.MDString("Dwarf Version"),
 414  				llvm.ConstInt(c.ctx.Int32Type(), 4, false).ConstantAsMetadata(),
 415  			}),
 416  		)
 417  		if c.MoxieVersion != "" {
 418  			// It is necessary to set llvm.ident, otherwise debugging on MacOS
 419  			// won't work.
 420  			c.mod.AddNamedMetadataOperand("llvm.ident",
 421  				c.ctx.MDNode(([]llvm.Metadata{
 422  					c.ctx.MDString("Moxie version " + c.MoxieVersion),
 423  				})))
 424  		}
 425  		c.dibuilder.Finalize()
 426  		c.dibuilder.Destroy()
 427  	}
 428  
 429  	// Add the "target-abi" flag, which is necessary on RISC-V otherwise it will
 430  	// pick one that doesn't match the -mabi Clang flag.
 431  	if c.ABI != "" {
 432  		c.mod.AddNamedMetadataOperand("llvm.module.flags",
 433  			c.ctx.MDNode([]llvm.Metadata{
 434  				llvm.ConstInt(c.ctx.Int32Type(), 1, false).ConstantAsMetadata(), // Error on mismatch
 435  				c.ctx.MDString("target-abi"),
 436  				c.ctx.MDString(c.ABI),
 437  			}),
 438  		)
 439  	}
 440  
 441  	return c.mod, c.diagnostics
 442  }
 443  
 444  func (c *compilerContext) getRuntimeType(name string) types.Type {
 445  	obj := c.runtimePkg.Scope().Lookup(name)
 446  	if obj == nil {
 447  		panic(fmt.Sprintf("runtime type %q not found in scope (pkg=%s, scope has %d names)", name, c.runtimePkg.Path(), c.runtimePkg.Scope().Len()))
 448  	}
 449  	return obj.(*types.TypeName).Type()
 450  }
 451  
 452  // emitLogAlloc emits runtime.logAlloc(siteID) at a heap allocation site
 453  // when the function name matches the -print-allocs pattern.
 454  func (b *builder) emitLogAlloc(pos token.Pos) {
 455  	if !b.PrintAllocs.MatchString(b.fn.RelString(nil)) {
 456  		return
 457  	}
 458  	if b.program.ImportedPackage("runtime").Members["logAlloc"] == nil {
 459  		return
 460  	}
 461  	position := b.program.Fset.Position(pos)
 462  	siteName := b.fn.RelString(nil) + "@" + position.Filename + ":" + strconv.Itoa(position.Line)
 463  	siteIDVal := llvm.ConstInt(b.ctx.Int32Type(), uint64(nextAllocSite(siteName)), false)
 464  	b.createRuntimeCall("logAlloc", []llvm.Value{siteIDVal}, "")
 465  }
 466  
 467  // getLLVMRuntimeType obtains a named type from the runtime package and returns
 468  // it as a LLVM type, creating it if necessary. It is a shorthand for
 469  // getLLVMType(getRuntimeType(name)).
 470  func (c *compilerContext) getLLVMRuntimeType(name string) llvm.Type {
 471  	return c.getLLVMType(c.getRuntimeType(name))
 472  }
 473  
 474  // getLLVMType returns a LLVM type for a Go type. It doesn't recreate already
 475  // created types. This is somewhat important for performance, but especially
 476  // important for named struct types (which should only be created once).
 477  func (c *compilerContext) getLLVMType(goType types.Type) llvm.Type {
 478  	// Try to load the LLVM type from the cache.
 479  	// Note: *types.Named isn't unique when working with generics.
 480  	// See https://github.com/golang/go/issues/53914
 481  	// This is the reason for using typeutil.Map to lookup LLVM types for Go types.
 482  	ival := c.llvmTypes.At(goType)
 483  	if ival != nil {
 484  		return ival.(llvm.Type)
 485  	}
 486  	// Not already created, so adding this type to the cache.
 487  	llvmType := c.makeLLVMType(goType)
 488  	c.llvmTypes.Set(goType, llvmType)
 489  	return llvmType
 490  }
 491  
 492  // makeLLVMType creates a LLVM type for a Go type. Don't call this, use
 493  // getLLVMType instead.
 494  func (c *compilerContext) makeLLVMType(goType types.Type) llvm.Type {
 495  	switch typ := types.Unalias(goType).(type) {
 496  	case *types.Array:
 497  		elemType := c.getLLVMType(typ.Elem())
 498  		return llvm.ArrayType(elemType, int(typ.Len()))
 499  	case *types.Basic:
 500  		switch typ.Kind() {
 501  		case types.Bool, types.UntypedBool:
 502  			return c.ctx.Int1Type()
 503  		case types.Int8, types.Uint8:
 504  			return c.ctx.Int8Type()
 505  		case types.Int16, types.Uint16:
 506  			return c.ctx.Int16Type()
 507  		case types.Int32, types.Uint32:
 508  			return c.ctx.Int32Type()
 509  		case types.Int, types.Uint:
 510  			return c.intType
 511  		case types.Int64, types.Uint64:
 512  			return c.ctx.Int64Type()
 513  		case types.Float32:
 514  			return c.ctx.FloatType()
 515  		case types.Float64:
 516  			return c.ctx.DoubleType()
 517  		case types.Complex64:
 518  			return c.ctx.StructType([]llvm.Type{c.ctx.FloatType(), c.ctx.FloatType()}, false)
 519  		case types.Complex128:
 520  			return c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false)
 521  		case types.String, types.UntypedString:
 522  			return c.getLLVMRuntimeType("_string")
 523  		case types.Uintptr:
 524  			return c.uintptrType
 525  		case types.UnsafePointer:
 526  			return c.dataPtrType
 527  		default:
 528  			panic("unknown basic type: " + typ.String())
 529  		}
 530  	case *types.Chan, *types.Map, *types.Pointer:
 531  		return c.dataPtrType // all pointers are the same
 532  	case *types.Interface:
 533  		return c.getLLVMRuntimeType("_interface")
 534  	case *types.Named:
 535  		if st, ok := typ.Underlying().(*types.Struct); ok {
 536  			// Structs are a special case. While other named types are ignored
 537  			// in LLVM IR, named structs are implemented as named structs in
 538  			// LLVM. This is because it is otherwise impossible to create
 539  			// self-referencing types such as linked lists.
 540  			llvmName := typ.String()
 541  			llvmType := c.ctx.StructCreateNamed(llvmName)
 542  			c.llvmTypes.Set(goType, llvmType) // avoid infinite recursion
 543  			underlying := c.getLLVMType(st)
 544  			llvmType.StructSetBody(underlying.StructElementTypes(), false)
 545  			return llvmType
 546  		}
 547  		return c.getLLVMType(typ.Underlying())
 548  	case *types.Signature: // function value
 549  		return c.getFuncType(typ)
 550  	case *types.Slice:
 551  		// Moxie: []byte uses the named _string type (string=[]byte unification).
 552  		if basic, ok := typ.Elem().(*types.Basic); ok && basic.Kind() == types.Byte {
 553  			return c.getLLVMRuntimeType("_string")
 554  		}
 555  		members := []llvm.Type{
 556  			c.dataPtrType,
 557  			c.uintptrType, // len
 558  			c.uintptrType, // cap
 559  		}
 560  		return c.ctx.StructType(members, false)
 561  	case *types.Struct:
 562  		members := make([]llvm.Type, typ.NumFields())
 563  		for i := 0; i < typ.NumFields(); i++ {
 564  			members[i] = c.getLLVMType(typ.Field(i).Type())
 565  		}
 566  		return c.ctx.StructType(members, false)
 567  	case *types.TypeParam:
 568  		return c.getLLVMType(typ.Underlying())
 569  	case *types.Tuple:
 570  		members := make([]llvm.Type, typ.Len())
 571  		for i := 0; i < typ.Len(); i++ {
 572  			members[i] = c.getLLVMType(typ.At(i).Type())
 573  		}
 574  		return c.ctx.StructType(members, false)
 575  	default:
 576  		panic("unknown type: " + goType.String())
 577  	}
 578  }
 579  
 580  // Is this a pointer type of some sort? Can be unsafe.Pointer or any *T pointer.
 581  func isPointer(typ types.Type) bool {
 582  	if _, ok := typ.(*types.Pointer); ok {
 583  		return true
 584  	} else if typ, ok := typ.(*types.Basic); ok && typ.Kind() == types.UnsafePointer {
 585  		return true
 586  	} else {
 587  		return false
 588  	}
 589  }
 590  
 591  // Get the DWARF type for this Go type.
 592  func (c *compilerContext) getDIType(typ types.Type) llvm.Metadata {
 593  	if md, ok := c.ditypes[typ]; ok {
 594  		return md
 595  	}
 596  	md := c.createDIType(typ)
 597  	c.ditypes[typ] = md
 598  	return md
 599  }
 600  
 601  // createDIType creates a new DWARF type. Don't call this function directly,
 602  // call getDIType instead.
 603  func (c *compilerContext) createDIType(typ types.Type) llvm.Metadata {
 604  	llvmType := c.getLLVMType(typ)
 605  	sizeInBytes := c.targetData.TypeAllocSize(llvmType)
 606  	switch typ := typ.(type) {
 607  	case *types.Alias:
 608  		// Implement types.Alias just like types.Named: by treating them like a
 609  		// C typedef.
 610  		temporaryMDNode := c.dibuilder.CreateReplaceableCompositeType(llvm.Metadata{}, llvm.DIReplaceableCompositeType{
 611  			Tag:         dwarf.TagTypedef,
 612  			SizeInBits:  sizeInBytes * 8,
 613  			AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
 614  		})
 615  		c.ditypes[typ] = temporaryMDNode
 616  		md := c.dibuilder.CreateTypedef(llvm.DITypedef{
 617  			Type: c.getDIType(types.Unalias(typ)), // TODO: use typ.Rhs in Go 1.23
 618  			Name: typ.String(),
 619  		})
 620  		temporaryMDNode.ReplaceAllUsesWith(md)
 621  		return md
 622  	case *types.Array:
 623  		return c.dibuilder.CreateArrayType(llvm.DIArrayType{
 624  			SizeInBits:  sizeInBytes * 8,
 625  			AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
 626  			ElementType: c.getDIType(typ.Elem()),
 627  			Subscripts: []llvm.DISubrange{
 628  				{
 629  					Lo:    0,
 630  					Count: typ.Len(),
 631  				},
 632  			},
 633  		})
 634  	case *types.Basic:
 635  		var encoding llvm.DwarfTypeEncoding
 636  		if typ.Info()&types.IsBoolean != 0 {
 637  			encoding = llvm.DW_ATE_boolean
 638  		} else if typ.Info()&types.IsFloat != 0 {
 639  			encoding = llvm.DW_ATE_float
 640  		} else if typ.Info()&types.IsComplex != 0 {
 641  			encoding = llvm.DW_ATE_complex_float
 642  		} else if typ.Info()&types.IsUnsigned != 0 {
 643  			encoding = llvm.DW_ATE_unsigned
 644  		} else if typ.Info()&types.IsInteger != 0 {
 645  			encoding = llvm.DW_ATE_signed
 646  		} else if typ.Kind() == types.UnsafePointer {
 647  			return c.dibuilder.CreatePointerType(llvm.DIPointerType{
 648  				Name:         "unsafe.Pointer",
 649  				SizeInBits:   c.targetData.TypeAllocSize(llvmType) * 8,
 650  				AlignInBits:  uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
 651  				AddressSpace: 0,
 652  			})
 653  		} else if typ.Info()&types.IsString != 0 {
 654  			return c.dibuilder.CreateStructType(llvm.Metadata{}, llvm.DIStructType{
 655  				Name:        "string",
 656  				SizeInBits:  sizeInBytes * 8,
 657  				AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
 658  				Elements: []llvm.Metadata{
 659  					c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
 660  						Name:         "ptr",
 661  						SizeInBits:   c.targetData.TypeAllocSize(c.dataPtrType) * 8,
 662  						AlignInBits:  uint32(c.targetData.ABITypeAlignment(c.dataPtrType)) * 8,
 663  						OffsetInBits: 0,
 664  						Type:         c.getDIType(types.NewPointer(types.Typ[types.Byte])),
 665  					}),
 666  					c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
 667  						Name:         "len",
 668  						SizeInBits:   c.targetData.TypeAllocSize(c.uintptrType) * 8,
 669  						AlignInBits:  uint32(c.targetData.ABITypeAlignment(c.uintptrType)) * 8,
 670  						OffsetInBits: c.targetData.ElementOffset(llvmType, 1) * 8,
 671  						Type:         c.getDIType(types.Typ[types.Uintptr]),
 672  					}),
 673  					c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
 674  						Name:         "cap",
 675  						SizeInBits:   c.targetData.TypeAllocSize(c.uintptrType) * 8,
 676  						AlignInBits:  uint32(c.targetData.ABITypeAlignment(c.uintptrType)) * 8,
 677  						OffsetInBits: c.targetData.ElementOffset(llvmType, 2) * 8,
 678  						Type:         c.getDIType(types.Typ[types.Uintptr]),
 679  					}),
 680  				},
 681  			})
 682  		} else {
 683  			panic("unknown basic type")
 684  		}
 685  		return c.dibuilder.CreateBasicType(llvm.DIBasicType{
 686  			Name:       typ.String(),
 687  			SizeInBits: sizeInBytes * 8,
 688  			Encoding:   encoding,
 689  		})
 690  	case *types.Chan:
 691  		return c.getDIType(types.NewPointer(c.program.ImportedPackage("runtime").Members["channel"].(*ssa.Type).Type()))
 692  	case *types.Interface:
 693  		return c.getDIType(c.program.ImportedPackage("runtime").Members["_interface"].(*ssa.Type).Type())
 694  	case *types.Map:
 695  		return c.getDIType(types.NewPointer(c.program.ImportedPackage("runtime").Members["hashmap"].(*ssa.Type).Type()))
 696  	case *types.Named:
 697  		// Placeholder metadata node, to be replaced afterwards.
 698  		temporaryMDNode := c.dibuilder.CreateReplaceableCompositeType(llvm.Metadata{}, llvm.DIReplaceableCompositeType{
 699  			Tag:         dwarf.TagTypedef,
 700  			SizeInBits:  sizeInBytes * 8,
 701  			AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
 702  		})
 703  		c.ditypes[typ] = temporaryMDNode
 704  		md := c.dibuilder.CreateTypedef(llvm.DITypedef{
 705  			Type: c.getDIType(typ.Underlying()),
 706  			Name: typ.String(),
 707  		})
 708  		temporaryMDNode.ReplaceAllUsesWith(md)
 709  		return md
 710  	case *types.Pointer:
 711  		return c.dibuilder.CreatePointerType(llvm.DIPointerType{
 712  			Pointee:      c.getDIType(typ.Elem()),
 713  			SizeInBits:   c.targetData.TypeAllocSize(llvmType) * 8,
 714  			AlignInBits:  uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
 715  			AddressSpace: 0,
 716  		})
 717  	case *types.Signature:
 718  		// actually a closure
 719  		fields := llvmType.StructElementTypes()
 720  		return c.dibuilder.CreateStructType(llvm.Metadata{}, llvm.DIStructType{
 721  			SizeInBits:  sizeInBytes * 8,
 722  			AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
 723  			Elements: []llvm.Metadata{
 724  				c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
 725  					Name:         "context",
 726  					SizeInBits:   c.targetData.TypeAllocSize(fields[1]) * 8,
 727  					AlignInBits:  uint32(c.targetData.ABITypeAlignment(fields[1])) * 8,
 728  					OffsetInBits: 0,
 729  					Type:         c.getDIType(types.Typ[types.UnsafePointer]),
 730  				}),
 731  				c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
 732  					Name:         "fn",
 733  					SizeInBits:   c.targetData.TypeAllocSize(fields[0]) * 8,
 734  					AlignInBits:  uint32(c.targetData.ABITypeAlignment(fields[0])) * 8,
 735  					OffsetInBits: c.targetData.ElementOffset(llvmType, 1) * 8,
 736  					Type:         c.getDIType(types.Typ[types.UnsafePointer]),
 737  				}),
 738  			},
 739  		})
 740  	case *types.Slice:
 741  		fields := llvmType.StructElementTypes()
 742  		return c.dibuilder.CreateStructType(llvm.Metadata{}, llvm.DIStructType{
 743  			Name:        typ.String(),
 744  			SizeInBits:  sizeInBytes * 8,
 745  			AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
 746  			Elements: []llvm.Metadata{
 747  				c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
 748  					Name:         "ptr",
 749  					SizeInBits:   c.targetData.TypeAllocSize(fields[0]) * 8,
 750  					AlignInBits:  uint32(c.targetData.ABITypeAlignment(fields[0])) * 8,
 751  					OffsetInBits: 0,
 752  					Type:         c.getDIType(types.NewPointer(typ.Elem())),
 753  				}),
 754  				c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
 755  					Name:         "len",
 756  					SizeInBits:   c.targetData.TypeAllocSize(c.uintptrType) * 8,
 757  					AlignInBits:  uint32(c.targetData.ABITypeAlignment(c.uintptrType)) * 8,
 758  					OffsetInBits: c.targetData.ElementOffset(llvmType, 1) * 8,
 759  					Type:         c.getDIType(types.Typ[types.Uintptr]),
 760  				}),
 761  				c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
 762  					Name:         "cap",
 763  					SizeInBits:   c.targetData.TypeAllocSize(c.uintptrType) * 8,
 764  					AlignInBits:  uint32(c.targetData.ABITypeAlignment(c.uintptrType)) * 8,
 765  					OffsetInBits: c.targetData.ElementOffset(llvmType, 2) * 8,
 766  					Type:         c.getDIType(types.Typ[types.Uintptr]),
 767  				}),
 768  			},
 769  		})
 770  	case *types.Struct:
 771  		elements := make([]llvm.Metadata, typ.NumFields())
 772  		for i := range elements {
 773  			field := typ.Field(i)
 774  			fieldType := field.Type()
 775  			llvmField := c.getLLVMType(fieldType)
 776  			elements[i] = c.dibuilder.CreateMemberType(llvm.Metadata{}, llvm.DIMemberType{
 777  				Name:         field.Name(),
 778  				SizeInBits:   c.targetData.TypeAllocSize(llvmField) * 8,
 779  				AlignInBits:  uint32(c.targetData.ABITypeAlignment(llvmField)) * 8,
 780  				OffsetInBits: c.targetData.ElementOffset(llvmType, i) * 8,
 781  				Type:         c.getDIType(fieldType),
 782  			})
 783  		}
 784  		md := c.dibuilder.CreateStructType(llvm.Metadata{}, llvm.DIStructType{
 785  			SizeInBits:  sizeInBytes * 8,
 786  			AlignInBits: uint32(c.targetData.ABITypeAlignment(llvmType)) * 8,
 787  			Elements:    elements,
 788  		})
 789  		return md
 790  	case *types.TypeParam:
 791  		return c.getDIType(typ.Underlying())
 792  	default:
 793  		panic("unknown type while generating DWARF debug type: " + typ.String())
 794  	}
 795  }
 796  
 797  // setDebugLocation sets the current debug location for the builder.
 798  func (b *builder) setDebugLocation(pos token.Pos) {
 799  	if pos == token.NoPos {
 800  		// No debug information available for this instruction.
 801  		b.SetCurrentDebugLocation(0, 0, b.difunc, llvm.Metadata{})
 802  		return
 803  	}
 804  
 805  	position := b.program.Fset.Position(pos)
 806  	if b.fn.Synthetic == "package initializer" {
 807  		// Package initializers are treated specially, because while individual
 808  		// Go SSA instructions have file/line/col information, the parent
 809  		// function does not. LLVM doesn't store filename information per
 810  		// instruction, only per function. We work around this difference by
 811  		// creating a fake DIFunction for each Go file and say that the
 812  		// instruction really came from that (fake) function but was inlined in
 813  		// the package initializer function.
 814  		position := b.program.Fset.Position(pos)
 815  		name := filepath.Base(position.Filename)
 816  		difunc, ok := b.initPseudoFuncs[name]
 817  		if !ok {
 818  			diFuncType := b.dibuilder.CreateSubroutineType(llvm.DISubroutineType{
 819  				File: b.getDIFile(position.Filename),
 820  			})
 821  			difunc = b.dibuilder.CreateFunction(b.getDIFile(position.Filename), llvm.DIFunction{
 822  				Name:         b.fn.RelString(nil) + "#" + name,
 823  				File:         b.getDIFile(position.Filename),
 824  				Line:         0,
 825  				Type:         diFuncType,
 826  				LocalToUnit:  true,
 827  				IsDefinition: true,
 828  				ScopeLine:    0,
 829  				Flags:        llvm.FlagPrototyped,
 830  				Optimized:    true,
 831  			})
 832  			b.initPseudoFuncs[name] = difunc
 833  		}
 834  		b.SetCurrentDebugLocation(uint(position.Line), uint(position.Column), difunc, b.initInlinedAt)
 835  		return
 836  	}
 837  
 838  	// Regular debug information.
 839  	b.SetCurrentDebugLocation(uint(position.Line), uint(position.Column), b.difunc, llvm.Metadata{})
 840  }
 841  
 842  // getLocalVariable returns a debug info entry for a local variable, which may
 843  // either be a parameter or a regular variable. It will create a new metadata
 844  // entry if there isn't one for the variable yet.
 845  func (b *builder) getLocalVariable(variable *types.Var) llvm.Metadata {
 846  	if dilocal, ok := b.dilocals[variable]; ok {
 847  		// DILocalVariable was already created, return it directly.
 848  		return dilocal
 849  	}
 850  
 851  	pos := b.program.Fset.Position(variable.Pos())
 852  
 853  	// Check whether this is a function parameter.
 854  	for i, param := range b.fn.Params {
 855  		if param.Object().(*types.Var) == variable {
 856  			// Yes it is, create it as a function parameter.
 857  			dilocal := b.dibuilder.CreateParameterVariable(b.difunc, llvm.DIParameterVariable{
 858  				Name:           param.Name(),
 859  				File:           b.getDIFile(pos.Filename),
 860  				Line:           pos.Line,
 861  				Type:           b.getDIType(param.Type()),
 862  				AlwaysPreserve: true,
 863  				ArgNo:          i + 1,
 864  			})
 865  			b.dilocals[variable] = dilocal
 866  			return dilocal
 867  		}
 868  	}
 869  
 870  	// No, it's not a parameter. Create a regular (auto) variable.
 871  	dilocal := b.dibuilder.CreateAutoVariable(b.difunc, llvm.DIAutoVariable{
 872  		Name:           variable.Name(),
 873  		File:           b.getDIFile(pos.Filename),
 874  		Line:           pos.Line,
 875  		Type:           b.getDIType(variable.Type()),
 876  		AlwaysPreserve: true,
 877  	})
 878  	b.dilocals[variable] = dilocal
 879  	return dilocal
 880  }
 881  
 882  // attachDebugInfo adds debug info to a function declaration. It returns the
 883  // DISubprogram metadata node.
 884  func (c *compilerContext) attachDebugInfo(f *ssa.Function) llvm.Metadata {
 885  	pos := c.program.Fset.Position(f.Syntax().Pos())
 886  	_, fn := c.getFunction(f)
 887  	return c.attachDebugInfoRaw(f, fn, "", pos.Filename, pos.Line)
 888  }
 889  
 890  // attachDebugInfo adds debug info to a function declaration. It returns the
 891  // DISubprogram metadata node. This method allows some more control over how
 892  // debug info is added to the function.
 893  func (c *compilerContext) attachDebugInfoRaw(f *ssa.Function, llvmFn llvm.Value, suffix, filename string, line int) llvm.Metadata {
 894  	// Debug info for this function.
 895  	params := getParams(f.Signature)
 896  	diparams := make([]llvm.Metadata, 0, len(params))
 897  	for _, param := range params {
 898  		diparams = append(diparams, c.getDIType(param.Type()))
 899  	}
 900  	diFuncType := c.dibuilder.CreateSubroutineType(llvm.DISubroutineType{
 901  		File:       c.getDIFile(filename),
 902  		Parameters: diparams,
 903  		Flags:      0, // ?
 904  	})
 905  	difunc := c.dibuilder.CreateFunction(c.getDIFile(filename), llvm.DIFunction{
 906  		Name:         f.RelString(nil) + suffix,
 907  		LinkageName:  c.getFunctionInfo(f).linkName + suffix,
 908  		File:         c.getDIFile(filename),
 909  		Line:         line,
 910  		Type:         diFuncType,
 911  		LocalToUnit:  true,
 912  		IsDefinition: true,
 913  		ScopeLine:    0,
 914  		Flags:        llvm.FlagPrototyped,
 915  		Optimized:    true,
 916  	})
 917  	llvmFn.SetSubprogram(difunc)
 918  	return difunc
 919  }
 920  
 921  // getDIFile returns a DIFile metadata node for the given filename. It tries to
 922  // use one that was already created, otherwise it falls back to creating a new
 923  // one.
 924  func (c *compilerContext) getDIFile(filename string) llvm.Metadata {
 925  	if _, ok := c.difiles[filename]; !ok {
 926  		dir, file := filepath.Split(filename)
 927  		if dir != "" {
 928  			dir = dir[:len(dir)-1]
 929  		}
 930  		c.difiles[filename] = c.dibuilder.CreateFile(file, dir)
 931  	}
 932  	return c.difiles[filename]
 933  }
 934  
 935  // createPackage builds the LLVM IR for all types, methods, and global variables
 936  // in the given package.
 937  func (c *compilerContext) createPackage(irbuilder llvm.Builder, pkg *ssa.Package) {
 938  	// Sort by position, so that the order of the functions in the IR matches
 939  	// the order of functions in the source file. This is useful for testing,
 940  	// for example.
 941  	var members []string
 942  	for name := range pkg.Members {
 943  		members = append(members, name)
 944  	}
 945  	sort.Slice(members, func(i, j int) bool {
 946  		iPos := pkg.Members[members[i]].Pos()
 947  		jPos := pkg.Members[members[j]].Pos()
 948  		if i == j {
 949  			// Cannot sort by pos, so do it by name.
 950  			return members[i] < members[j]
 951  		}
 952  		return iPos < jPos
 953  	})
 954  
 955  	// Define all functions.
 956  	for _, name := range members {
 957  		member := pkg.Members[name]
 958  		switch member := member.(type) {
 959  		case *ssa.Function:
 960  			if member.TypeParams() != nil {
 961  				// Do not try to build generic (non-instantiated) functions.
 962  				continue
 963  			}
 964  			// Create the function definition.
 965  			b := newBuilder(c, irbuilder, member)
 966  			if _, ok := mathToLLVMMapping[member.RelString(nil)]; ok {
 967  				// The body of this function (if there is one) is ignored and
 968  				// replaced with a LLVM intrinsic call.
 969  				b.defineMathOp()
 970  				continue
 971  			}
 972  			if ok := b.defineMathBitsIntrinsic(); ok {
 973  				// Like a math intrinsic, the body of this function was replaced
 974  				// with a LLVM intrinsic.
 975  				continue
 976  			}
 977  			if member.Blocks == nil {
 978  				// Try to define this as an intrinsic function.
 979  				b.defineIntrinsicFunction()
 980  				// It might not be an intrinsic function but simply an external
 981  				// function (defined via //go:linkname). Leave it undefined in
 982  				// that case.
 983  				continue
 984  			}
 985  			b.createFunction()
 986  		case *ssa.Type:
 987  			if types.IsInterface(member.Type()) {
 988  				// Interfaces don't have concrete methods.
 989  				continue
 990  			}
 991  			if _, isalias := member.Type().(*types.Alias); isalias {
 992  				// Aliases don't need to be redefined, since they just refer to
 993  				// an already existing type whose methods will be defined.
 994  				continue
 995  			}
 996  
 997  			// Named type. We should make sure all methods are created.
 998  			// This includes both functions with pointer receivers and those
 999  			// without.
1000  			methods := getAllMethods(pkg.Prog, member.Type())
1001  			methods = append(methods, getAllMethods(pkg.Prog, types.NewPointer(member.Type()))...)
1002  			for _, method := range methods {
1003  				// Parse this method.
1004  				fn := pkg.Prog.MethodValue(method)
1005  				if fn == nil {
1006  					continue // probably a generic method
1007  				}
1008  				if member.Type().String() != member.String() {
1009  					// This is a member on a type alias. Do not build such a
1010  					// function.
1011  					continue
1012  				}
1013  				if fn.Blocks == nil {
1014  					continue // external function
1015  				}
1016  				if fn.Synthetic != "" && fn.Synthetic != "package initializer" {
1017  					// This function is a kind of wrapper function (created by
1018  					// the ssa package, not appearing in the source code) that
1019  					// is created by the getFunction method as needed.
1020  					// Therefore, don't build it here to avoid "function
1021  					// redeclared" errors.
1022  					continue
1023  				}
1024  				// Create the function definition.
1025  				b := newBuilder(c, irbuilder, fn)
1026  				b.createFunction()
1027  			}
1028  		case *ssa.Global:
1029  			// Global variable.
1030  			info := c.getGlobalInfo(member)
1031  			global := c.getGlobal(member)
1032  			if files, ok := c.embedGlobals[member.Name()]; ok {
1033  				c.createEmbedGlobal(member, global, files)
1034  			} else if !info.extern {
1035  				global.SetInitializer(llvm.ConstNull(global.GlobalValueType()))
1036  				global.SetVisibility(llvm.HiddenVisibility)
1037  				if info.section != "" {
1038  					global.SetSection(info.section)
1039  				}
1040  			}
1041  		}
1042  	}
1043  
1044  	// Add forwarding functions for functions that would otherwise be
1045  	// implemented in assembly.
1046  	for _, name := range members {
1047  		member := pkg.Members[name]
1048  		switch member := member.(type) {
1049  		case *ssa.Function:
1050  			if member.Blocks != nil {
1051  				continue // external function
1052  			}
1053  			info := c.getFunctionInfo(member)
1054  			if aliasName, ok := stdlibAliases[info.linkName]; ok {
1055  				alias := c.mod.NamedFunction(aliasName)
1056  				if alias.IsNil() {
1057  					// Shouldn't happen, but perhaps best to just ignore.
1058  					// The error will be a link error, if there is an error.
1059  					continue
1060  				}
1061  				b := newBuilder(c, irbuilder, member)
1062  				b.createAlias(alias)
1063  			}
1064  		}
1065  	}
1066  }
1067  
1068  // createEmbedGlobal creates an initializer for a //go:embed global variable.
1069  func (c *compilerContext) createEmbedGlobal(member *ssa.Global, global llvm.Value, files []*loader.EmbedFile) {
1070  	switch typ := member.Type().(*types.Pointer).Elem().Underlying().(type) {
1071  	case *types.Basic:
1072  		// String type.
1073  		if typ.Kind() != types.String {
1074  			// This is checked at the AST level, so should be unreachable.
1075  			panic("expected a string type")
1076  		}
1077  		if len(files) != 1 {
1078  			c.addError(member.Pos(), fmt.Sprintf("//go:embed for a string should be given exactly one file, got %d", len(files)))
1079  			return
1080  		}
1081  		strObj := c.getEmbedFileString(files[0])
1082  		global.SetInitializer(strObj)
1083  		global.SetVisibility(llvm.HiddenVisibility)
1084  
1085  	case *types.Slice:
1086  		if typ.Elem().Underlying().(*types.Basic).Kind() != types.Byte {
1087  			// This is checked at the AST level, so should be unreachable.
1088  			panic("expected a byte slice")
1089  		}
1090  		if len(files) != 1 {
1091  			c.addError(member.Pos(), fmt.Sprintf("//go:embed for a string should be given exactly one file, got %d", len(files)))
1092  			return
1093  		}
1094  		file := files[0]
1095  		bufferValue := c.ctx.ConstString(string(file.Data), false)
1096  		bufferGlobal := llvm.AddGlobal(c.mod, bufferValue.Type(), c.pkg.Path()+"$embedslice")
1097  		bufferGlobal.SetInitializer(bufferValue)
1098  		bufferGlobal.SetLinkage(llvm.InternalLinkage)
1099  		bufferGlobal.SetAlignment(1)
1100  		slicePtr := llvm.ConstInBoundsGEP(bufferValue.Type(), bufferGlobal, []llvm.Value{
1101  			llvm.ConstInt(c.uintptrType, 0, false),
1102  			llvm.ConstInt(c.uintptrType, 0, false),
1103  		})
1104  		sliceLen := llvm.ConstInt(c.uintptrType, file.Size, false)
1105  		sliceObj := llvm.ConstNamedStruct(c.getLLVMRuntimeType("_string"), []llvm.Value{slicePtr, sliceLen, sliceLen})
1106  		global.SetInitializer(sliceObj)
1107  		global.SetVisibility(llvm.HiddenVisibility)
1108  
1109  		if c.Debug {
1110  			// Add debug info to the slice backing array.
1111  			position := c.program.Fset.Position(member.Pos())
1112  			diglobal := c.dibuilder.CreateGlobalVariableExpression(llvm.Metadata{}, llvm.DIGlobalVariableExpression{
1113  				File:        c.getDIFile(position.Filename),
1114  				Line:        position.Line,
1115  				Type:        c.getDIType(types.NewArray(types.Typ[types.Byte], int64(len(file.Data)))),
1116  				LocalToUnit: true,
1117  				Expr:        c.dibuilder.CreateExpression(nil),
1118  			})
1119  			bufferGlobal.AddMetadata(0, diglobal)
1120  		}
1121  
1122  	case *types.Struct:
1123  		// Assume this is an embed.FS struct:
1124  		// https://cs.opensource.google/go/go/+/refs/tags/go1.18.2:src/embed/embed.go;l=148
1125  		// It looks like this:
1126  		//   type FS struct {
1127  		//       files *file
1128  		//   }
1129  
1130  		// Make a slice of the files, as they will appear in the binary. They
1131  		// are sorted in a special way to allow for binary searches, see
1132  		// src/embed/embed.go for details.
1133  		dirset := map[string]struct{}{}
1134  		var allFiles []*loader.EmbedFile
1135  		for _, file := range files {
1136  			allFiles = append(allFiles, file)
1137  			dirname := file.Name
1138  			for {
1139  				dirname, _ = path.Split(path.Clean(dirname))
1140  				if dirname == "" {
1141  					break
1142  				}
1143  				if _, ok := dirset[dirname]; ok {
1144  					break
1145  				}
1146  				dirset[dirname] = struct{}{}
1147  				allFiles = append(allFiles, &loader.EmbedFile{
1148  					Name: dirname,
1149  				})
1150  			}
1151  		}
1152  		sort.Slice(allFiles, func(i, j int) bool {
1153  			dir1, name1 := path.Split(path.Clean(allFiles[i].Name))
1154  			dir2, name2 := path.Split(path.Clean(allFiles[j].Name))
1155  			if dir1 != dir2 {
1156  				return dir1 < dir2
1157  			}
1158  			return name1 < name2
1159  		})
1160  
1161  		// Make the backing array for the []files slice. This is a LLVM global.
1162  		embedFileStructType := typ.Field(0).Type().(*types.Pointer).Elem().(*types.Slice).Elem()
1163  		llvmEmbedFileStructType := c.getLLVMType(embedFileStructType)
1164  		var fileStructs []llvm.Value
1165  		for _, file := range allFiles {
1166  			fileStruct := llvm.ConstNull(llvmEmbedFileStructType)
1167  			name := c.createConst(ssa.NewConst(constant.MakeString(file.Name), types.Typ[types.String]), getPos(member))
1168  			fileStruct = c.builder.CreateInsertValue(fileStruct, name, 0, "") // "name" field
1169  			if file.Hash != "" {
1170  				data := c.getEmbedFileString(file)
1171  				fileStruct = c.builder.CreateInsertValue(fileStruct, data, 1, "") // "data" field
1172  			}
1173  			fileStructs = append(fileStructs, fileStruct)
1174  		}
1175  		sliceDataInitializer := llvm.ConstArray(llvmEmbedFileStructType, fileStructs)
1176  		sliceDataGlobal := llvm.AddGlobal(c.mod, sliceDataInitializer.Type(), c.pkg.Path()+"$embedfsfiles")
1177  		sliceDataGlobal.SetInitializer(sliceDataInitializer)
1178  		sliceDataGlobal.SetLinkage(llvm.InternalLinkage)
1179  		sliceDataGlobal.SetGlobalConstant(true)
1180  		sliceDataGlobal.SetUnnamedAddr(true)
1181  		sliceDataGlobal.SetAlignment(c.targetData.ABITypeAlignment(sliceDataInitializer.Type()))
1182  		if c.Debug {
1183  			// Add debug information for code size attribution (among others).
1184  			position := c.program.Fset.Position(member.Pos())
1185  			diglobal := c.dibuilder.CreateGlobalVariableExpression(llvm.Metadata{}, llvm.DIGlobalVariableExpression{
1186  				File:        c.getDIFile(position.Filename),
1187  				Line:        position.Line,
1188  				Type:        c.getDIType(types.NewArray(embedFileStructType, int64(len(allFiles)))),
1189  				LocalToUnit: true,
1190  				Expr:        c.dibuilder.CreateExpression(nil),
1191  			})
1192  			sliceDataGlobal.AddMetadata(0, diglobal)
1193  		}
1194  
1195  		// Create the slice object itself.
1196  		// Because embed.FS refers to it as *[]embed.file instead of a plain
1197  		// []embed.file, we have to store this as a global.
1198  		slicePtr := llvm.ConstInBoundsGEP(sliceDataInitializer.Type(), sliceDataGlobal, []llvm.Value{
1199  			llvm.ConstInt(c.uintptrType, 0, false),
1200  			llvm.ConstInt(c.uintptrType, 0, false),
1201  		})
1202  		sliceLen := llvm.ConstInt(c.uintptrType, uint64(len(fileStructs)), false)
1203  		sliceInitializer := c.ctx.ConstStruct([]llvm.Value{slicePtr, sliceLen, sliceLen}, false)
1204  		sliceGlobal := llvm.AddGlobal(c.mod, sliceInitializer.Type(), c.pkg.Path()+"$embedfsslice")
1205  		sliceGlobal.SetInitializer(sliceInitializer)
1206  		sliceGlobal.SetLinkage(llvm.InternalLinkage)
1207  		sliceGlobal.SetGlobalConstant(true)
1208  		sliceGlobal.SetUnnamedAddr(true)
1209  		sliceGlobal.SetAlignment(c.targetData.ABITypeAlignment(sliceInitializer.Type()))
1210  		if c.Debug {
1211  			position := c.program.Fset.Position(member.Pos())
1212  			diglobal := c.dibuilder.CreateGlobalVariableExpression(llvm.Metadata{}, llvm.DIGlobalVariableExpression{
1213  				File:        c.getDIFile(position.Filename),
1214  				Line:        position.Line,
1215  				Type:        c.getDIType(types.NewSlice(embedFileStructType)),
1216  				LocalToUnit: true,
1217  				Expr:        c.dibuilder.CreateExpression(nil),
1218  			})
1219  			sliceGlobal.AddMetadata(0, diglobal)
1220  		}
1221  
1222  		// Define the embed.FS struct. It has only one field: the files (as a
1223  		// *[]embed.file).
1224  		globalInitializer := llvm.ConstNull(c.getLLVMType(member.Type().(*types.Pointer).Elem()))
1225  		globalInitializer = c.builder.CreateInsertValue(globalInitializer, sliceGlobal, 0, "")
1226  		global.SetInitializer(globalInitializer)
1227  		global.SetVisibility(llvm.HiddenVisibility)
1228  		global.SetAlignment(c.targetData.ABITypeAlignment(globalInitializer.Type()))
1229  	}
1230  }
1231  
1232  // getEmbedFileString returns the (constant) string object with the contents of
1233  // the given file. This is a llvm.Value of a regular Go string.
1234  func (c *compilerContext) getEmbedFileString(file *loader.EmbedFile) llvm.Value {
1235  	dataGlobalName := "embed/file_" + file.Hash
1236  	dataGlobal := c.mod.NamedGlobal(dataGlobalName)
1237  	dataGlobalType := llvm.ArrayType(c.ctx.Int8Type(), int(file.Size))
1238  	if dataGlobal.IsNil() {
1239  		dataGlobal = llvm.AddGlobal(c.mod, dataGlobalType, dataGlobalName)
1240  	}
1241  	strPtr := llvm.ConstInBoundsGEP(dataGlobalType, dataGlobal, []llvm.Value{
1242  		llvm.ConstInt(c.uintptrType, 0, false),
1243  		llvm.ConstInt(c.uintptrType, 0, false),
1244  	})
1245  	strLen := llvm.ConstInt(c.uintptrType, file.Size, false)
1246  	return llvm.ConstNamedStruct(c.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen, strLen})
1247  }
1248  
1249  // Start defining a function so that it can be filled with instructions: load
1250  // parameters, create basic blocks, and set up debug information.
1251  // This is separated out from createFunction() so that it is also usable to
1252  // define compiler intrinsics like the atomic operations in sync/atomic.
1253  func (b *builder) createFunctionStart(intrinsic bool) {
1254  	if b.DumpSSA {
1255  		fmt.Printf("\nfunc %s:\n", b.fn)
1256  	}
1257  	if !b.llvmFn.IsDeclaration() {
1258  		errValue := b.llvmFn.Name() + " redeclared in this program"
1259  		fnPos := getPosition(b.llvmFn)
1260  		if fnPos.IsValid() {
1261  			errValue += "\n\tprevious declaration at " + fnPos.String()
1262  		}
1263  		b.addError(b.fn.Pos(), errValue)
1264  		return
1265  	}
1266  
1267  	b.addStandardDefinedAttributes(b.llvmFn)
1268  	if !b.info.exported && !b.StandaloneRuntime {
1269  		// Do not set visibility for local linkage (internal or private).
1270  		// Otherwise a "local linkage requires default visibility"
1271  		// assertion error in llvm-project/llvm/include/llvm/IR/GlobalValue.h:236
1272  		// is thrown.
1273  		if b.llvmFn.Linkage() != llvm.InternalLinkage &&
1274  			b.llvmFn.Linkage() != llvm.PrivateLinkage {
1275  			b.llvmFn.SetVisibility(llvm.HiddenVisibility)
1276  		}
1277  		b.llvmFn.SetUnnamedAddr(true)
1278  	}
1279  	if b.info.section != "" {
1280  		b.llvmFn.SetSection(b.info.section)
1281  	}
1282  	if b.info.exported {
1283  		if strings.HasPrefix(b.Triple, "wasm") {
1284  			// Set the exported name. This is necessary for WebAssembly because
1285  			// otherwise the function is not exported.
1286  			functionAttr := b.ctx.CreateStringAttribute("wasm-export-name", b.info.linkName)
1287  			b.llvmFn.AddFunctionAttr(functionAttr)
1288  		}
1289  		if strings.HasPrefix(b.Triple, "wasm") || b.BuildMode == "c-shared" {
1290  			// LTO generally optimizes away functions not called from within
1291  			// the module. Exported functions must be explicitly marked as used.
1292  			llvmutil.AppendToGlobal(b.mod, "llvm.used", b.llvmFn)
1293  		}
1294  	}
1295  
1296  	// Some functions have a pragma controlling the inlining level.
1297  	switch b.info.inline {
1298  	case inlineHint:
1299  		// Add LLVM inline hint to functions with //go:inline pragma.
1300  		inline := b.ctx.CreateEnumAttribute(llvm.AttributeKindID("inlinehint"), 0)
1301  		b.llvmFn.AddFunctionAttr(inline)
1302  	case inlineNone:
1303  		// Add LLVM attribute to always avoid inlining this function.
1304  		noinline := b.ctx.CreateEnumAttribute(llvm.AttributeKindID("noinline"), 0)
1305  		b.llvmFn.AddFunctionAttr(noinline)
1306  	}
1307  
1308  	if b.info.interrupt {
1309  		// Mark this function as an interrupt.
1310  		// This is necessary on MCUs that don't push caller saved registers when
1311  		// entering an interrupt, such as on AVR.
1312  		if strings.HasPrefix(b.Triple, "avr") {
1313  			b.llvmFn.AddFunctionAttr(b.ctx.CreateStringAttribute("signal", ""))
1314  		} else {
1315  			b.addError(b.fn.Pos(), "//go:interrupt not supported on this architecture")
1316  		}
1317  	}
1318  
1319  	// Add debug info, if needed.
1320  	if b.Debug {
1321  		if b.fn.Synthetic == "package initializer" {
1322  			// Package initializer functions have no debug info. Create some
1323  			// fake debug info to at least have *something*.
1324  			b.difunc = b.attachDebugInfoRaw(b.fn, b.llvmFn, "", b.packageDir, 0)
1325  		} else if b.fn.Syntax() != nil {
1326  			// Create debug info file if needed.
1327  			b.difunc = b.attachDebugInfo(b.fn)
1328  		}
1329  		b.setDebugLocation(b.fn.Pos())
1330  	}
1331  
1332  	// Pre-create all basic blocks in the function.
1333  	var entryBlock llvm.BasicBlock
1334  	if intrinsic {
1335  		// This function isn't defined in Go SSA. It is probably a compiler
1336  		// intrinsic (like an atomic operation). Create the entry block
1337  		// manually.
1338  		entryBlock = b.ctx.AddBasicBlock(b.llvmFn, "entry")
1339  		// Intrinsics may create internal branches (e.g. nil checks).
1340  		// They will attempt to access b.currentBlockInfo to update the exit block.
1341  		// Create some fake block info for them to access.
1342  		blockInfo := []blockInfo{
1343  			{
1344  				entry: entryBlock,
1345  				exit:  entryBlock,
1346  			},
1347  		}
1348  		b.blockInfo = blockInfo
1349  		b.currentBlockInfo = &blockInfo[0]
1350  	} else {
1351  		blocks := b.fn.Blocks
1352  		blockInfo := make([]blockInfo, len(blocks))
1353  		for _, block := range b.fn.DomPreorder() {
1354  			info := &blockInfo[block.Index]
1355  			llvmBlock := b.ctx.AddBasicBlock(b.llvmFn, block.Comment)
1356  			info.entry = llvmBlock
1357  			info.exit = llvmBlock
1358  		}
1359  		b.blockInfo = blockInfo
1360  		// Normal functions have an entry block.
1361  		entryBlock = blockInfo[0].entry
1362  	}
1363  	b.SetInsertPointAtEnd(entryBlock)
1364  
1365  	if b.fn.Synthetic == "package initializer" {
1366  		b.initPseudoFuncs = make(map[string]llvm.Metadata)
1367  
1368  		// Create a fake 'inlined at' metadata node.
1369  		// See setDebugLocation for details.
1370  		alloca := b.CreateAlloca(b.uintptrType, "")
1371  		b.initInlinedAt = alloca.InstructionDebugLoc()
1372  		alloca.EraseFromParentAsInstruction()
1373  	}
1374  
1375  	// Load function parameters
1376  	llvmParamIndex := 0
1377  	for _, param := range b.fn.Params {
1378  		llvmType := b.getLLVMType(param.Type())
1379  		fields := make([]llvm.Value, 0, 1)
1380  		for _, info := range b.expandFormalParamType(llvmType, param.Name(), param.Type()) {
1381  			param := b.llvmFn.Param(llvmParamIndex)
1382  			param.SetName(info.name)
1383  			fields = append(fields, param)
1384  			llvmParamIndex++
1385  		}
1386  		b.locals[param] = b.collapseFormalParam(llvmType, fields)
1387  
1388  		// Add debug information to this parameter (if available)
1389  		// Note: setupWasmSpawnTargetChannels is called after all params are loaded.
1390  		if b.Debug && b.fn.Syntax() != nil {
1391  			dbgParam := b.getLocalVariable(param.Object().(*types.Var))
1392  			loc := b.GetCurrentDebugLocation()
1393  			if len(fields) == 1 {
1394  				expr := b.dibuilder.CreateExpression(nil)
1395  				b.dibuilder.InsertValueAtEnd(fields[0], dbgParam, expr, loc, entryBlock)
1396  			} else {
1397  				fieldOffsets := b.expandFormalParamOffsets(llvmType)
1398  				for i, field := range fields {
1399  					expr := b.dibuilder.CreateExpression([]uint64{
1400  						0x1000,              // DW_OP_LLVM_fragment
1401  						fieldOffsets[i] * 8, // offset in bits
1402  						b.targetData.TypeAllocSize(field.Type()) * 8, // size in bits
1403  					})
1404  					b.dibuilder.InsertValueAtEnd(field, dbgParam, expr, loc, entryBlock)
1405  				}
1406  			}
1407  		}
1408  	}
1409  
1410  	// For wasm spawn targets, mark channel params as SAB-backed in b.sabChannels.
1411  	b.setupWasmSpawnTargetChannels()
1412  
1413  	// For native spawn targets, mark channel params as pipe-bound to ChildPipeFd.
1414  	b.setupNativeSpawnTargetChannels()
1415  
1416  	// Load free variables from the context. This is a closure (or bound
1417  	// method).
1418  	var context llvm.Value
1419  	if !b.info.exported {
1420  		context = b.llvmFn.LastParam()
1421  		context.SetName("context")
1422  	}
1423  	if len(b.fn.FreeVars) != 0 {
1424  		// Get a list of all variable types in the context.
1425  		freeVarTypes := make([]llvm.Type, len(b.fn.FreeVars))
1426  		for i, freeVar := range b.fn.FreeVars {
1427  			freeVarTypes[i] = b.getLLVMType(freeVar.Type())
1428  		}
1429  
1430  		// Load each free variable from the context pointer.
1431  		// A free variable is always a pointer when this is a closure, but it
1432  		// can be another type when it is a wrapper for a bound method (these
1433  		// wrappers are generated by the ssa package).
1434  		for i, val := range b.emitPointerUnpack(context, freeVarTypes) {
1435  			b.locals[b.fn.FreeVars[i]] = val
1436  		}
1437  	}
1438  
1439  	if b.fn.Recover != nil {
1440  		// This function has deferred function calls. Set some things up for
1441  		// them.
1442  		b.deferInitFunc()
1443  	}
1444  
1445  	// Per-function arena DISABLED for bootstrap.
1446  	// The type universe is a graph, not a tree. Deep-copy-on-return cannot
1447  	// preserve shared references (TypeName <-> Named, Scope.elems -> Object).
1448  	// Per-function arena + incomplete deep copy causes SIGSEGV in hashmapGet
1449  	// when Scope.Insert's arena frees string key data still referenced by
1450  	// the map. All allocations go to the global arena (freed at process exit).
1451  	// Stage4 will implement per-phase arenas with correct lifecycle.
1452  }
1453  
1454  // createFunction builds the LLVM IR implementation for this function. The
1455  // function must not yet be defined, otherwise this function will create a
1456  // diagnostic.
1457  func (b *builder) createFunction() {
1458  	b.createFunctionStart(false)
1459  
1460  	// Check Moxie language restrictions on user code.
1461  	b.checkMoxieRestrictions()
1462  
1463  	// Fill blocks with instructions.
1464  	for _, block := range b.fn.DomPreorder() {
1465  		if b.DumpSSA {
1466  			fmt.Printf("%d: %s:\n", block.Index, block.Comment)
1467  		}
1468  		b.currentBlock = block
1469  		b.currentBlockInfo = &b.blockInfo[block.Index]
1470  		b.SetInsertPointAtEnd(b.currentBlockInfo.entry)
1471  		for _, instr := range block.Instrs {
1472  			if instr, ok := instr.(*ssa.DebugRef); ok {
1473  				if !b.Debug {
1474  					continue
1475  				}
1476  				object := instr.Object()
1477  				variable, ok := object.(*types.Var)
1478  				if !ok {
1479  					// Not a local variable.
1480  					continue
1481  				}
1482  				if instr.IsAddr {
1483  					// TODO, this may happen for *ssa.Alloc and *ssa.FieldAddr
1484  					// for example.
1485  					continue
1486  				}
1487  				dbgVar := b.getLocalVariable(variable)
1488  				pos := b.program.Fset.Position(instr.Pos())
1489  				b.dibuilder.InsertValueAtEnd(b.getValue(instr.X, getPos(instr)), dbgVar, b.dibuilder.CreateExpression(nil), llvm.DebugLoc{
1490  					Line:  uint(pos.Line),
1491  					Col:   uint(pos.Column),
1492  					Scope: b.difunc,
1493  				}, b.GetInsertBlock())
1494  				continue
1495  			}
1496  			if b.DumpSSA {
1497  				if val, ok := instr.(ssa.Value); ok && val.Name() != "" {
1498  					fmt.Printf("\t%s = %s\n", val.Name(), val.String())
1499  				} else {
1500  					fmt.Printf("\t%s\n", instr.String())
1501  				}
1502  			}
1503  			b.createInstruction(instr)
1504  		}
1505  		if b.fn.Name() == "init" && len(block.Instrs) == 0 {
1506  			b.CreateRetVoid()
1507  		}
1508  	}
1509  
1510  	// The rundefers instruction needs to be created after all defer
1511  	// instructions have been created. Otherwise it won't handle all defer
1512  	// cases.
1513  	for i, bb := range b.runDefersBlock {
1514  		b.SetInsertPointAtEnd(bb)
1515  		b.createRunDefers()
1516  		b.CreateBr(b.afterDefersBlock[i])
1517  	}
1518  
1519  	if b.hasDeferFrame() {
1520  		// Create the landing pad block, where execution continues after a
1521  		// panic.
1522  		b.createLandingPad()
1523  	}
1524  
1525  	// Resolve phi nodes
1526  	for _, phi := range b.phis {
1527  		block := phi.ssa.Block()
1528  		for i, edge := range phi.ssa.Edges {
1529  			llvmVal := b.getValue(edge, getPos(phi.ssa))
1530  			predIdx := block.Preds[i].Index
1531  			llvmBlock := b.blockInfo[predIdx].exit
1532  				phi.llvm.AddIncoming([]llvm.Value{llvmVal}, []llvm.BasicBlock{llvmBlock})
1533  		}
1534  	}
1535  
1536  	// Create anonymous functions (closures etc.).
1537  	for _, sub := range b.fn.AnonFuncs {
1538  		b := newBuilder(b.compilerContext, b.Builder, sub)
1539  		b.llvmFn.SetLinkage(llvm.InternalLinkage)
1540  		b.createFunction()
1541  	}
1542  
1543  	// Create wrapper function that can be called externally.
1544  	if b.info.wasmExport != "" {
1545  		b.createWasmExport()
1546  	}
1547  }
1548  
1549  // posser is an interface that's implemented by both ssa.Value and
1550  // ssa.Instruction. It is implemented by everything that has a Pos() method,
1551  // which is all that getPos() needs.
1552  type posser interface {
1553  	Pos() token.Pos
1554  }
1555  
1556  // getPos returns position information for a ssa.Value or ssa.Instruction.
1557  //
1558  // Not all instructions have position information, especially when they're
1559  // implicit (such as implicit casts or implicit returns at the end of a
1560  // function). In these cases, it makes sense to try a bit harder to guess what
1561  // the position really should be.
1562  func getPos(val posser) token.Pos {
1563  	pos := val.Pos()
1564  	if pos != token.NoPos {
1565  		// Easy: position is known.
1566  		return pos
1567  	}
1568  
1569  	// No position information is known.
1570  	switch val := val.(type) {
1571  	case *ssa.MakeInterface:
1572  		return getPos(val.X)
1573  	case *ssa.MakeClosure:
1574  		return val.Fn.(*ssa.Function).Pos()
1575  	case *ssa.Return:
1576  		syntax := val.Parent().Syntax()
1577  		if syntax != nil {
1578  			// non-synthetic
1579  			return syntax.End()
1580  		}
1581  		return token.NoPos
1582  	case *ssa.FieldAddr:
1583  		return getPos(val.X)
1584  	case *ssa.IndexAddr:
1585  		return getPos(val.X)
1586  	case *ssa.Slice:
1587  		return getPos(val.X)
1588  	case *ssa.Store:
1589  		return getPos(val.Addr)
1590  	case *ssa.Extract:
1591  		return getPos(val.Tuple)
1592  	default:
1593  		// This is reachable, for example with *ssa.Const, *ssa.If, and
1594  		// *ssa.Jump. They might be implemented in some way in the future.
1595  		return token.NoPos
1596  	}
1597  }
1598  
1599  // createInstruction builds the LLVM IR equivalent instructions for the
1600  // particular Go SSA instruction.
1601  func (b *builder) createInstruction(instr ssa.Instruction) {
1602  	if b.Debug {
1603  		b.setDebugLocation(getPos(instr))
1604  	}
1605  
1606  	switch instr := instr.(type) {
1607  	case ssa.Value:
1608  		if value, err := b.createExpr(instr); err != nil {
1609  			// This expression could not be parsed. Add the error to the list
1610  			// of diagnostics and continue with an undef value.
1611  			// The resulting IR will be incorrect (but valid). However,
1612  			// compilation can proceed which is useful because there may be
1613  			// more compilation errors which can then all be shown together to
1614  			// the user.
1615  			b.diagnostics = append(b.diagnostics, err)
1616  			b.locals[instr] = llvm.Undef(b.getLLVMType(instr.Type()))
1617  		} else {
1618  			b.locals[instr] = value
1619  		}
1620  	case *ssa.DebugRef:
1621  		// ignore
1622  	case *ssa.Defer:
1623  		b.createDefer(instr)
1624  	case *ssa.Go:
1625  		// Moxie: go keyword banned in user code. Runtime/internal exempt.
1626  		if isUserPackage(b.fn.Pkg) {
1627  			b.addError(instr.Pos(), "moxie: the go keyword is not supported (there are no goroutines)")
1628  		} else {
1629  			b.createGo(instr)
1630  		}
1631  	case *ssa.If:
1632  		cond := b.getValue(instr.Cond, getPos(instr))
1633  		blockThen := b.blockInfo[instr.Block().Succs[0].Index].entry
1634  		blockElse := b.blockInfo[instr.Block().Succs[1].Index].entry
1635  		b.CreateCondBr(cond, blockThen, blockElse)
1636  	case *ssa.Jump:
1637  		blockJump := b.blockInfo[instr.Block().Succs[0].Index].entry
1638  		b.CreateBr(blockJump)
1639  	case *ssa.MapUpdate:
1640  		m := b.getValue(instr.Map, getPos(instr))
1641  		key := b.getValue(instr.Key, getPos(instr))
1642  		value := b.getValue(instr.Value, getPos(instr))
1643  		mapType := instr.Map.Type().Underlying().(*types.Map)
1644  		b.createMapUpdate(mapType.Key(), m, key, value, instr.Pos())
1645  	case *ssa.Panic:
1646  		value := b.getValue(instr.X, getPos(instr))
1647  		b.createRuntimeInvoke("_panic", []llvm.Value{value}, "")
1648  		b.CreateUnreachable()
1649  	case *ssa.Return:
1650  		if b.hasDeferFrame() {
1651  			b.createRuntimeCall("destroyDeferFrame", []llvm.Value{b.deferFrame}, "")
1652  		}
1653  		isRuntime := b.fn.Pkg != nil && b.fn.Pkg.Pkg.Path() == "runtime"
1654  		hasArena := !isRuntime && !b.fnArenaVal.IsNil()
1655  		if len(instr.Results) == 0 {
1656  			if hasArena {
1657  				b.createRuntimeCall("SetCurrentArena", []llvm.Value{b.outerArenaVal}, "")
1658  				b.createRuntimeCall("ArenaFree", []llvm.Value{b.fnArenaVal}, "")
1659  			}
1660  			b.CreateRetVoid()
1661  		} else if len(instr.Results) == 1 {
1662  			val := b.getValue(instr.Results[0], getPos(instr))
1663  			if hasArena {
1664  				// Switch to caller's arena. Source data in fn_arena stays intact.
1665  				b.createRuntimeCall("SetCurrentArena", []llvm.Value{b.outerArenaVal}, "")
1666  				val = b.emitReturnCopy(val, instr.Results[0])
1667  				b.createRuntimeCall("ArenaFree", []llvm.Value{b.fnArenaVal}, "")
1668  			}
1669  			b.CreateRet(val)
1670  		} else {
1671  			retVal := llvm.ConstNull(b.llvmFn.GlobalValueType().ReturnType())
1672  			for i, result := range instr.Results {
1673  				val := b.getValue(result, getPos(instr))
1674  				retVal = b.CreateInsertValue(retVal, val, i, "")
1675  			}
1676  			if hasArena {
1677  				b.createRuntimeCall("SetCurrentArena", []llvm.Value{b.outerArenaVal}, "")
1678  				for i, result := range instr.Results {
1679  					if typeContainsPointers(result.Type()) && b.valueIsLocal(result) {
1680  						field := b.CreateExtractValue(retVal, i, "ret.f")
1681  						copied := b.emitReturnCopyValue(field, result.Type())
1682  						retVal = b.CreateInsertValue(retVal, copied, i, "ret.f")
1683  					}
1684  				}
1685  				b.createRuntimeCall("ArenaFree", []llvm.Value{b.fnArenaVal}, "")
1686  			}
1687  			b.CreateRet(retVal)
1688  		}
1689  	case *ssa.RunDefers:
1690  		// Note where we're going to put the rundefers block
1691  		run := b.insertBasicBlock("rundefers.block")
1692  		b.CreateBr(run)
1693  		b.runDefersBlock = append(b.runDefersBlock, run)
1694  
1695  		after := b.insertBasicBlock("rundefers.after")
1696  		b.SetInsertPointAtEnd(after)
1697  		b.afterDefersBlock = append(b.afterDefersBlock, after)
1698  	case *ssa.Send:
1699  		b.createChanSend(instr)
1700  	case *ssa.Store:
1701  		llvmAddr := b.getValue(instr.Addr, getPos(instr))
1702  		llvmVal := b.getValue(instr.Val, getPos(instr))
1703  		b.createNilCheck(instr.Addr, llvmAddr, "store")
1704  		if b.targetData.TypeAllocSize(llvmVal.Type()) == 0 {
1705  			// nothing to store
1706  			return
1707  		}
1708  		b.CreateStore(llvmVal, llvmAddr)
1709  	default:
1710  		b.addError(instr.Pos(), "unknown instruction: "+instr.String())
1711  	}
1712  }
1713  
1714  // sliceAllocType returns the slice/string underlying type for an Alloc,
1715  // or nil if the alloc is not for a slice/string.
1716  func (b *builder) sliceAllocType(expr *ssa.Alloc) types.Type {
1717  	pt, ok := expr.Type().(*types.Pointer)
1718  	if !ok {
1719  		return nil
1720  	}
1721  	u := pt.Elem().Underlying()
1722  	switch u.(type) {
1723  	case *types.Slice:
1724  		return u
1725  	case *types.Basic:
1726  		bb := u.(*types.Basic)
1727  		if bb.Kind() == types.String || bb.Kind() == types.UntypedString {
1728  			return u
1729  		}
1730  	}
1731  	return nil
1732  }
1733  
1734  // makeDefaultSlice creates a {alloc(4096*elemSize), 0, 4096} slice value.
1735  func (b *builder) makeDefaultSlice(sl *types.Slice, pos token.Pos) llvm.Value {
1736  	capVal := uint64(4096)
1737  	et := b.getLLVMType(sl.Elem())
1738  	elemSize := b.targetData.TypeAllocSize(et)
1739  	sizeVal := llvm.ConstInt(b.uintptrType, capVal*elemSize, false)
1740  	layoutVal := b.createObjectLayout(et, pos)
1741  	dataBuf := b.createRuntimeCall("alloc", []llvm.Value{sizeVal, layoutVal}, "slice.cap4k")
1742  	typ := b.getLLVMType(sl)
1743  	sliceVal := llvm.Undef(typ)
1744  	sliceVal = b.CreateInsertValue(sliceVal, dataBuf, 0, "")
1745  	sliceVal = b.CreateInsertValue(sliceVal, llvm.ConstInt(b.uintptrType, 0, false), 1, "")
1746  	sliceVal = b.CreateInsertValue(sliceVal, llvm.ConstInt(b.uintptrType, capVal, false), 2, "")
1747  	return sliceVal
1748  }
1749  
1750  func (b *builder) makeDefaultStringSlice(pos token.Pos) llvm.Value {
1751  	capVal := uint64(4096)
1752  	sizeVal := llvm.ConstInt(b.uintptrType, capVal, false)
1753  	layoutVal := llvm.ConstNull(b.dataPtrType)
1754  	dataBuf := b.createRuntimeCall("alloc", []llvm.Value{sizeVal, layoutVal}, "str.cap4k")
1755  	typ := b.getLLVMRuntimeType("_string")
1756  	sliceVal := llvm.Undef(typ)
1757  	sliceVal = b.CreateInsertValue(sliceVal, dataBuf, 0, "")
1758  	sliceVal = b.CreateInsertValue(sliceVal, llvm.ConstInt(b.uintptrType, 0, false), 1, "")
1759  	sliceVal = b.CreateInsertValue(sliceVal, llvm.ConstInt(b.uintptrType, capVal, false), 2, "")
1760  	return sliceVal
1761  }
1762  
1763  // createBuiltin lowers a builtin Go function (append, close, delete, etc.) to
1764  // LLVM IR. It uses runtime calls for some builtins.
1765  func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, callName string, pos token.Pos) (llvm.Value, error) {
1766  	switch callName {
1767  	case "append":
1768  		src := argValues[0]
1769  		elems := argValues[1]
1770  		srcBuf := b.CreateExtractValue(src, 0, "append.srcBuf")
1771  		srcLen := b.CreateExtractValue(src, 1, "append.srcLen")
1772  		srcCap := b.CreateExtractValue(src, 2, "append.srcCap")
1773  		elemsBuf := b.CreateExtractValue(elems, 0, "append.elemsBuf")
1774  		elemsLen := b.CreateExtractValue(elems, 1, "append.elemsLen")
1775  		// Moxie: argTypes[0] may be *types.Basic (string) due to string=[]byte.
1776  		var elemType llvm.Type
1777  		switch ut := argTypes[0].Underlying().(type) {
1778  		case *types.Slice:
1779  			elemType = b.getLLVMType(ut.Elem())
1780  		case *types.Basic: // string=[]byte: element is byte
1781  			elemType = b.ctx.Int8Type()
1782  		default:
1783  			panic("append on non-slice type: " + argTypes[0].String())
1784  		}
1785  		elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false)
1786  		result := b.createRuntimeCall("sliceAppend", []llvm.Value{srcBuf, elemsBuf, srcLen, srcCap, elemsLen, elemSize}, "append.new")
1787  		newPtr := b.CreateExtractValue(result, 0, "append.newPtr")
1788  		newLen := b.CreateExtractValue(result, 1, "append.newLen")
1789  		newCap := b.CreateExtractValue(result, 2, "append.newCap")
1790  		newSlice := llvm.Undef(src.Type())
1791  		newSlice = b.CreateInsertValue(newSlice, newPtr, 0, "")
1792  		newSlice = b.CreateInsertValue(newSlice, newLen, 1, "")
1793  		newSlice = b.CreateInsertValue(newSlice, newCap, 2, "")
1794  		return newSlice, nil
1795  	case "cap":
1796  		value := argValues[0]
1797  		var llvmCap llvm.Value
1798  		switch argTypes[0].Underlying().(type) {
1799  		case *types.Chan:
1800  			llvmCap = b.createRuntimeCall("chanCap", []llvm.Value{value}, "cap")
1801  		case *types.Slice:
1802  			llvmCap = b.CreateExtractValue(value, 2, "cap")
1803  		default:
1804  			return llvm.Value{}, b.makeError(pos, "todo: cap: unknown type")
1805  		}
1806  		capSize := b.targetData.TypeAllocSize(llvmCap.Type())
1807  		intSize := b.targetData.TypeAllocSize(b.intType)
1808  		if capSize < intSize {
1809  			llvmCap = b.CreateZExt(llvmCap, b.intType, "cap.int")
1810  		} else if capSize > intSize {
1811  			llvmCap = b.CreateTrunc(llvmCap, b.intType, "cap.int")
1812  		}
1813  		return llvmCap, nil
1814  	case "close":
1815  		b.createChanClose(argValues[0])
1816  		return llvm.Value{}, nil
1817  	case "complex":
1818  		r := argValues[0]
1819  		i := argValues[1]
1820  		t := argTypes[0].Underlying().(*types.Basic)
1821  		var cplx llvm.Value
1822  		switch t.Kind() {
1823  		case types.Float32:
1824  			cplx = llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.FloatType(), b.ctx.FloatType()}, false))
1825  		case types.Float64:
1826  			cplx = llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.DoubleType(), b.ctx.DoubleType()}, false))
1827  		default:
1828  			return llvm.Value{}, b.makeError(pos, "unsupported type in complex builtin: "+t.String())
1829  		}
1830  		cplx = b.CreateInsertValue(cplx, r, 0, "")
1831  		cplx = b.CreateInsertValue(cplx, i, 1, "")
1832  		return cplx, nil
1833  	case "clear":
1834  		value := argValues[0]
1835  		switch typ := argTypes[0].Underlying().(type) {
1836  		case *types.Slice:
1837  			elementType := b.getLLVMType(typ.Elem())
1838  			elementSize := b.targetData.TypeAllocSize(elementType)
1839  			elementAlign := b.targetData.ABITypeAlignment(elementType)
1840  
1841  			// The pointer to the data to be cleared.
1842  			llvmBuf := b.CreateExtractValue(value, 0, "buf")
1843  
1844  			// The length (in bytes) to be cleared.
1845  			llvmLen := b.CreateExtractValue(value, 1, "len")
1846  			llvmLen = b.CreateMul(llvmLen, llvm.ConstInt(llvmLen.Type(), elementSize, false), "")
1847  
1848  			// Do the clear operation using the LLVM memset builtin.
1849  			// This is also correct for nil slices: in those cases, len will be
1850  			// 0 which means the memset call is a no-op (according to the LLVM
1851  			// LangRef).
1852  			memset := b.getMemsetFunc()
1853  			call := b.createCall(memset.GlobalValueType(), memset, []llvm.Value{
1854  				llvmBuf, // dest
1855  				llvm.ConstInt(b.ctx.Int8Type(), 0, false), // val
1856  				llvmLen, // len
1857  				llvm.ConstInt(b.ctx.Int1Type(), 0, false), // isVolatile
1858  			}, "")
1859  			call.AddCallSiteAttribute(1, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(elementAlign)))
1860  
1861  			return llvm.Value{}, nil
1862  		case *types.Map:
1863  			m := argValues[0]
1864  			b.createMapClear(m)
1865  			return llvm.Value{}, nil
1866  		default:
1867  			return llvm.Value{}, b.makeError(pos, "unsupported type in clear builtin: "+typ.String())
1868  		}
1869  	case "copy":
1870  		dst := argValues[0]
1871  		src := argValues[1]
1872  		dstLen := b.CreateExtractValue(dst, 1, "copy.dstLen")
1873  		srcLen := b.CreateExtractValue(src, 1, "copy.srcLen")
1874  		dstBuf := b.CreateExtractValue(dst, 0, "copy.dstArray")
1875  		srcBuf := b.CreateExtractValue(src, 0, "copy.srcArray")
1876  		elemType := b.getLLVMType(argTypes[0].Underlying().(*types.Slice).Elem())
1877  		elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false)
1878  		return b.createRuntimeCall("sliceCopy", []llvm.Value{dstBuf, srcBuf, dstLen, srcLen, elemSize}, "copy.n"), nil
1879  	case "delete":
1880  		m := argValues[0]
1881  		key := argValues[1]
1882  		return llvm.Value{}, b.createMapDelete(argTypes[1], m, key, pos)
1883  	case "imag":
1884  		cplx := argValues[0]
1885  		return b.CreateExtractValue(cplx, 1, "imag"), nil
1886  	case "len":
1887  		value := argValues[0]
1888  		var llvmLen llvm.Value
1889  		switch argTypes[0].Underlying().(type) {
1890  		case *types.Basic, *types.Slice:
1891  			// string or slice
1892  			llvmLen = b.CreateExtractValue(value, 1, "len")
1893  		case *types.Chan:
1894  			llvmLen = b.createRuntimeCall("chanLen", []llvm.Value{value}, "len")
1895  		case *types.Map:
1896  			llvmLen = b.createRuntimeCall("hashmapLen", []llvm.Value{value}, "len")
1897  		default:
1898  			return llvm.Value{}, b.makeError(pos, "todo: len: unknown type")
1899  		}
1900  		lenSize := b.targetData.TypeAllocSize(llvmLen.Type())
1901  		intSize := b.targetData.TypeAllocSize(b.intType)
1902  		if lenSize < intSize {
1903  			llvmLen = b.CreateZExt(llvmLen, b.intType, "len.int")
1904  		} else if lenSize > intSize {
1905  			llvmLen = b.CreateTrunc(llvmLen, b.intType, "len.int")
1906  		}
1907  		return llvmLen, nil
1908  	case "min", "max":
1909  		// min and max builtins, added in Go 1.21.
1910  		// We can simply reuse the existing binop comparison code, which has all
1911  		// the edge cases figured out already.
1912  		tok := token.LSS
1913  		if callName == "max" {
1914  			tok = token.GTR
1915  		}
1916  		result := argValues[0]
1917  		typ := argTypes[0]
1918  		for _, arg := range argValues[1:] {
1919  			cmp, err := b.createBinOp(tok, typ, typ, result, arg, pos)
1920  			if err != nil {
1921  				return result, err
1922  			}
1923  			result = b.CreateSelect(cmp, result, arg, "")
1924  		}
1925  		return result, nil
1926  	case "push":
1927  		// push(s, v1, v2, ...) - lazy-init + bounds check via slicePush,
1928  		// then individual element stores via GEP. No sliceAppend (elements
1929  		// are passed individually, not as a slice).
1930  		src := argValues[0]
1931  		var elemType llvm.Type
1932  		switch ut := argTypes[0].Underlying().(type) {
1933  		case *types.Slice:
1934  			elemType = b.getLLVMType(ut.Elem())
1935  		case *types.Basic:
1936  			elemType = b.ctx.Int8Type()
1937  		default:
1938  			return llvm.Value{}, b.makeError(pos, "push on non-slice type")
1939  		}
1940  		elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false)
1941  		numElems := llvm.ConstInt(b.uintptrType, uint64(len(argValues)-1), false)
1942  		srcPtr := b.CreateExtractValue(src, 0, "push.ptr")
1943  		srcLen := b.CreateExtractValue(src, 1, "push.len")
1944  		srcCap := b.CreateExtractValue(src, 2, "push.cap")
1945  		// slicePush: nil-init cap 4096 + bounds check. Returns {ptr, newLen, cap}.
1946  		result := b.createRuntimeCall("slicePush", []llvm.Value{
1947  			srcPtr, srcLen, srcCap, numElems, elemSize,
1948  		}, "push.hdr")
1949  		newPtr := b.CreateExtractValue(result, 0, "push.newptr")
1950  		newLen := b.CreateExtractValue(result, 1, "push.newlen")
1951  		newCap := b.CreateExtractValue(result, 2, "push.newcap")
1952  		// Store each element at ptr[oldLen + i].
1953  		// If the slice element is an interface and the value is a concrete
1954  		// type, emit MakeInterface to create the proper {typecode, data} pair.
1955  		var sliceElemGoType types.Type
1956  		if sl, ok := argTypes[0].Underlying().(*types.Slice); ok {
1957  			sliceElemGoType = sl.Elem()
1958  		}
1959  		for i := 1; i < len(argValues); i++ {
1960  			val := argValues[i]
1961  			if sliceElemGoType != nil && types.IsInterface(sliceElemGoType) && !types.IsInterface(argTypes[i]) {
1962  				val = b.createMakeInterface(val, argTypes[i], pos)
1963  			}
1964  			idx := b.CreateAdd(srcLen, llvm.ConstInt(srcLen.Type(), uint64(i-1), false), "")
1965  			ep := b.CreateGEP(elemType, newPtr, []llvm.Value{idx}, "push.ep")
1966  			b.CreateStore(val, ep)
1967  		}
1968  		newSlice := llvm.Undef(src.Type())
1969  		newSlice = b.CreateInsertValue(newSlice, newPtr, 0, "")
1970  		newSlice = b.CreateInsertValue(newSlice, newLen, 1, "")
1971  		newSlice = b.CreateInsertValue(newSlice, newCap, 2, "")
1972  		return newSlice, nil
1973  	case "pop":
1974  		// pop(s) - return last element. Panics if len == 0.
1975  		// The store-back of the modified slice (len-1) is handled by
1976  		// the SSA builder's ExprStmt desugaring using resize.
1977  		src := argValues[0]
1978  		srcPtr := b.CreateExtractValue(src, 0, "pop.ptr")
1979  		srcLen := b.CreateExtractValue(src, 1, "pop.len")
1980  		zero := llvm.ConstInt(srcLen.Type(), 0, false)
1981  		empty := b.CreateICmp(llvm.IntEQ, srcLen, zero, "pop.empty")
1982  		b.createRuntimeAssert(empty, "pop", "slicePanic")
1983  		one := llvm.ConstInt(srcLen.Type(), 1, false)
1984  		lastIdx := b.CreateSub(srcLen, one, "pop.lastidx")
1985  		var elemType llvm.Type
1986  		switch ut := argTypes[0].Underlying().(type) {
1987  		case *types.Slice:
1988  			elemType = b.getLLVMType(ut.Elem())
1989  		case *types.Basic:
1990  			elemType = b.ctx.Int8Type()
1991  		default:
1992  			return llvm.Value{}, b.makeError(pos, "pop on non-slice type")
1993  		}
1994  		ep := b.CreateGEP(elemType, srcPtr, []llvm.Value{lastIdx}, "pop.ep")
1995  		elem := b.CreateLoad(elemType, ep, "pop.elem")
1996  		return elem, nil
1997  	case "resize":
1998  		// resize(s, n) - set len to n. Panics if n > cap or n < 0.
1999  		src := argValues[0]
2000  		srcCap := b.CreateExtractValue(src, 2, "resize.cap")
2001  		newLen := argValues[1]
2002  		// Bounds check: newLen <= cap (unsigned comparison catches n < 0).
2003  		overflow := b.CreateICmp(llvm.IntUGT, newLen, srcCap, "resize.overflow")
2004  		b.createRuntimeAssert(overflow, "resize", "slicePanic")
2005  		result := b.CreateInsertValue(src, newLen, 1, "resize.result")
2006  		return result, nil
2007  	case "panic":
2008  		// This is rare, but happens in "defer panic()".
2009  		b.createRuntimeInvoke("_panic", argValues, "")
2010  		return llvm.Value{}, nil
2011  	case "print", "println":
2012  		b.createRuntimeCall("printlock", nil, "")
2013  		for i, value := range argValues {
2014  			if i >= 1 && callName == "println" {
2015  				b.createRuntimeCall("printspace", nil, "")
2016  			}
2017  			typ := argTypes[i].Underlying()
2018  			switch typ := typ.(type) {
2019  			case *types.Basic:
2020  				switch typ.Kind() {
2021  				case types.String, types.UntypedString:
2022  					b.createRuntimeCall("printstring", []llvm.Value{value}, "")
2023  				case types.Uintptr:
2024  					b.createRuntimeCall("printptr", []llvm.Value{value}, "")
2025  				case types.UnsafePointer:
2026  					ptrValue := b.CreatePtrToInt(value, b.uintptrType, "")
2027  					b.createRuntimeCall("printptr", []llvm.Value{ptrValue}, "")
2028  				default:
2029  					// runtime.print{int,uint}{8,16,32,64}
2030  					if typ.Info()&types.IsInteger != 0 {
2031  						name := "print"
2032  						if typ.Info()&types.IsUnsigned != 0 {
2033  							name += "uint"
2034  						} else {
2035  							name += "int"
2036  						}
2037  						name += strconv.FormatUint(b.targetData.TypeAllocSize(value.Type())*8, 10)
2038  						b.createRuntimeCall(name, []llvm.Value{value}, "")
2039  					} else if typ.Kind() == types.Bool {
2040  						b.createRuntimeCall("printbool", []llvm.Value{value}, "")
2041  					} else if typ.Kind() == types.Float32 {
2042  						b.createRuntimeCall("printfloat32", []llvm.Value{value}, "")
2043  					} else if typ.Kind() == types.Float64 {
2044  						b.createRuntimeCall("printfloat64", []llvm.Value{value}, "")
2045  					} else if typ.Kind() == types.Complex64 {
2046  						b.createRuntimeCall("printcomplex64", []llvm.Value{value}, "")
2047  					} else if typ.Kind() == types.Complex128 {
2048  						b.createRuntimeCall("printcomplex128", []llvm.Value{value}, "")
2049  					} else {
2050  						return llvm.Value{}, b.makeError(pos, "unknown basic arg type: "+typ.String())
2051  					}
2052  				}
2053  			case *types.Interface:
2054  				b.createRuntimeCall("printitf", []llvm.Value{value}, "")
2055  			case *types.Map:
2056  				b.createRuntimeCall("printmap", []llvm.Value{value}, "")
2057  			case *types.Pointer:
2058  				ptrValue := b.CreatePtrToInt(value, b.uintptrType, "")
2059  				b.createRuntimeCall("printptr", []llvm.Value{ptrValue}, "")
2060  			case *types.Slice:
2061  				// []byte prints as text; other slices print the header.
2062  				if basic, ok := typ.Elem().(*types.Basic); ok && basic.Kind() == types.Byte {
2063  					b.createRuntimeCall("printbytes", []llvm.Value{value}, "")
2064  				} else {
2065  					bufptr := b.CreateExtractValue(value, 0, "")
2066  					buflen := b.CreateExtractValue(value, 1, "")
2067  					bufcap := b.CreateExtractValue(value, 2, "")
2068  					ptrValue := b.CreatePtrToInt(bufptr, b.uintptrType, "")
2069  					b.createRuntimeCall("printslice", []llvm.Value{ptrValue, buflen, bufcap}, "")
2070  				}
2071  			default:
2072  				return llvm.Value{}, b.makeError(pos, "unknown arg type: "+typ.String())
2073  			}
2074  		}
2075  		if callName == "println" {
2076  			b.createRuntimeCall("printnl", nil, "")
2077  		}
2078  		b.createRuntimeCall("printunlock", nil, "")
2079  		return llvm.Value{}, nil // print() or println() returns void
2080  	case "real":
2081  		cplx := argValues[0]
2082  		return b.CreateExtractValue(cplx, 0, "real"), nil
2083  	case "recover":
2084  		useParentFrame := uint64(0)
2085  		if b.hasDeferFrame() {
2086  			// recover() should return the panic value of the parent function,
2087  			// not of the current function.
2088  			useParentFrame = 1
2089  		}
2090  		return b.createRuntimeCall("_recover", []llvm.Value{llvm.ConstInt(b.ctx.Int1Type(), useParentFrame, false)}, ""), nil
2091  	case "ssa:wrapnilchk":
2092  		// TODO: do an actual nil check?
2093  		return argValues[0], nil
2094  
2095  	// Builtins from the unsafe package.
2096  	case "Add": // unsafe.Add
2097  		// This is basically just a GEP operation.
2098  		// Note: the pointer is always of type *i8.
2099  		ptr := argValues[0]
2100  		len := argValues[1]
2101  		return b.CreateGEP(b.ctx.Int8Type(), ptr, []llvm.Value{len}, ""), nil
2102  	case "Alignof": // unsafe.Alignof
2103  		align := b.targetData.ABITypeAlignment(argValues[0].Type())
2104  		return llvm.ConstInt(b.uintptrType, uint64(align), false), nil
2105  	case "Offsetof": // unsafe.Offsetof
2106  		// This builtin is a bit harder to implement and may need a bit of
2107  		// refactoring to work (it may be easier to implement if we have access
2108  		// to the underlying Go SSA instruction). It is also rarely used: it
2109  		// only applies in generic code and unsafe.Offsetof isn't very commonly
2110  		// used anyway.
2111  		// In other words, postpone it to some other day.
2112  		return llvm.Value{}, b.makeError(pos, "todo: unsafe.Offsetof")
2113  	case "Sizeof": // unsafe.Sizeof
2114  		size := b.targetData.TypeAllocSize(argValues[0].Type())
2115  		return llvm.ConstInt(b.uintptrType, size, false), nil
2116  	case "Slice", "String": // unsafe.Slice, unsafe.String
2117  		// This creates a slice or string from a pointer and a length.
2118  		// Note that the exception mentioned in the documentation (if the
2119  		// pointer and length are nil, the slice is also nil) is trivially
2120  		// already the case.
2121  		ptr := argValues[0]
2122  		len := argValues[1]
2123  		var elementType llvm.Type
2124  		if callName == "Slice" {
2125  			elementType = b.getLLVMType(argTypes[0].Underlying().(*types.Pointer).Elem())
2126  		} else {
2127  			elementType = b.ctx.Int8Type()
2128  		}
2129  		b.createUnsafeSliceStringCheck("unsafe."+callName, ptr, len, elementType, argTypes[1].Underlying().(*types.Basic))
2130  		if len.Type().IntTypeWidth() < b.uintptrType.IntTypeWidth() {
2131  			// Too small, zero-extend len.
2132  			len = b.CreateZExt(len, b.uintptrType, "")
2133  		} else if len.Type().IntTypeWidth() > b.uintptrType.IntTypeWidth() {
2134  			// Too big, truncate len.
2135  			len = b.CreateTrunc(len, b.uintptrType, "")
2136  		}
2137  		if callName == "Slice" {
2138  			// Moxie: detect []byte to use named _string type.
2139  			var sliceType llvm.Type
2140  			if elementType == b.ctx.Int8Type() {
2141  				sliceType = b.getLLVMRuntimeType("_string")
2142  			} else {
2143  				sliceType = b.ctx.StructType([]llvm.Type{
2144  					ptr.Type(),
2145  					b.uintptrType,
2146  					b.uintptrType,
2147  				}, false)
2148  			}
2149  			slice := llvm.Undef(sliceType)
2150  			slice = b.CreateInsertValue(slice, ptr, 0, "")
2151  			slice = b.CreateInsertValue(slice, len, 1, "")
2152  			slice = b.CreateInsertValue(slice, len, 2, "")
2153  			return slice, nil
2154  		} else {
2155  			str := llvm.Undef(b.getLLVMRuntimeType("_string"))
2156  			str = b.CreateInsertValue(str, argValues[0], 0, "")
2157  			str = b.CreateInsertValue(str, len, 1, "")
2158  			str = b.CreateInsertValue(str, len, 2, "") // cap = len for strings
2159  			return str, nil
2160  		}
2161  	case "SliceData", "StringData": // unsafe.SliceData, unsafe.StringData
2162  		return b.CreateExtractValue(argValues[0], 0, "slice.data"), nil
2163  	default:
2164  		return llvm.Value{}, b.makeError(pos, "todo: builtin: "+callName)
2165  	}
2166  }
2167  
2168  // createFunctionCall lowers a Go SSA call instruction (to a simple function,
2169  // closure, function pointer, builtin, method, etc.) to LLVM IR, usually a call
2170  // instruction.
2171  //
2172  // This is also where compiler intrinsics are implemented.
2173  func (b *builder) createFunctionCall(instr *ssa.CallCommon, callInstr *ssa.Call) (llvm.Value, error) {
2174  	// See if this is an intrinsic function that is handled specially.
2175  	if fn := instr.StaticCallee(); fn != nil {
2176  		// Direct function call, either to a named or anonymous (directly
2177  		// applied) function call. If it is anonymous, it may be a closure.
2178  		name := fn.RelString(nil)
2179  		switch {
2180  		case name == "device.Asm" || name == "device/arm.Asm" || name == "device/arm64.Asm" || name == "device/avr.Asm" || name == "device/riscv.Asm":
2181  			return b.createInlineAsm(instr.Args)
2182  		case name == "device.AsmFull" || name == "device/arm.AsmFull" || name == "device/arm64.AsmFull" || name == "device/avr.AsmFull" || name == "device/riscv.AsmFull":
2183  			return b.createInlineAsmFull(instr)
2184  		case strings.HasPrefix(name, "device/arm.SVCall"):
2185  			return b.emitSVCall(instr.Args, getPos(instr))
2186  		case strings.HasPrefix(name, "device/arm64.SVCall"):
2187  			return b.emitSV64Call(instr.Args, getPos(instr))
2188  		case strings.HasPrefix(name, "(device/riscv.CSR)."):
2189  			return b.emitCSROperation(instr)
2190  		case strings.HasPrefix(name, "syscall.Syscall") || strings.HasPrefix(name, "syscall.RawSyscall") || strings.HasPrefix(name, "golang.org/x/sys/unix.Syscall") || strings.HasPrefix(name, "golang.org/x/sys/unix.RawSyscall"):
2191  			if b.GOOS != "darwin" {
2192  				return b.createSyscall(instr)
2193  			}
2194  		case strings.HasPrefix(name, "syscall.rawSyscallNoError") || strings.HasPrefix(name, "golang.org/x/sys/unix.RawSyscallNoError"):
2195  			return b.createRawSyscallNoError(instr)
2196  		case name == "runtime.supportsRecover":
2197  			supportsRecover := uint64(0)
2198  			if b.supportsRecover() {
2199  				supportsRecover = 1
2200  			}
2201  			return llvm.ConstInt(b.ctx.Int1Type(), supportsRecover, false), nil
2202  		case name == "runtime.panicStrategy":
2203  			panicStrategy := map[string]uint64{
2204  				"print": moxie.PanicStrategyPrint,
2205  				"trap":  moxie.PanicStrategyTrap,
2206  			}[b.Config.PanicStrategy]
2207  			return llvm.ConstInt(b.ctx.Int8Type(), panicStrategy, false), nil
2208  		case name == "runtime/interrupt.New":
2209  			return b.createInterruptGlobal(instr)
2210  		case name == "runtime.exportedFuncPtr":
2211  			_, ptr := b.getFunction(instr.Args[0].(*ssa.Function))
2212  			return b.CreatePtrToInt(ptr, b.uintptrType, ""), nil
2213  		case name == "(*runtime/interrupt.Checkpoint).Save":
2214  			return b.createInterruptCheckpoint(instr.Args[0]), nil
2215  		case name == "internal/abi.FuncPCABI0":
2216  			retval := b.createDarwinFuncPCABI0Call(instr)
2217  			if !retval.IsNil() {
2218  				return retval, nil
2219  			}
2220  		case strings.HasPrefix(fn.Name(), "__moxie_concat_move"):
2221  			return b.createMoxieConcatMove(instr), nil
2222  		case strings.HasPrefix(fn.Name(), "__moxie_concat"):
2223  			return b.createMoxieConcat(instr, callInstr), nil
2224  		case strings.HasPrefix(fn.Name(), "__moxie_eq"):
2225  			return b.createMoxieEq(instr), nil
2226  		case strings.HasPrefix(fn.Name(), "__moxie_lt"):
2227  			return b.createMoxieLt(instr), nil
2228  		case strings.HasPrefix(fn.Name(), "__moxie_secalloc"):
2229  			return b.createMoxieSecalloc(instr), nil
2230  		}
2231  	}
2232  
2233  	// Dispatch builtins that need SSA-level analysis before generic arg lowering.
2234  	if call, ok := instr.Value.(*ssa.Builtin); ok {
2235  		switch call.Name() {
2236  		case "spawn":
2237  			return b.createSpawn(instr)
2238  		}
2239  	}
2240  
2241  	var params []llvm.Value
2242  	for _, param := range instr.Args {
2243  		params = append(params, b.getValue(param, getPos(instr)))
2244  	}
2245  
2246  	// Try to call the function directly for trivially static calls.
2247  	var callee, context llvm.Value
2248  	var calleeType llvm.Type
2249  	exported := false
2250  	if fn := instr.StaticCallee(); fn != nil {
2251  		calleeType, callee = b.getFunction(fn)
2252  		info := b.getFunctionInfo(fn)
2253  		if callee.IsNil() {
2254  			return llvm.Value{}, b.makeError(instr.Pos(), "undefined function: "+info.linkName)
2255  		}
2256  		switch value := instr.Value.(type) {
2257  		case *ssa.Function:
2258  			// Regular function call. No context is necessary.
2259  			context = llvm.Undef(b.dataPtrType)
2260  			if info.variadic && len(fn.Params) == 0 {
2261  				// This matches Clang, see: https://godbolt.org/z/Gqv49xKMq
2262  				// Eventually we might be able to eliminate this special case
2263  				// entirely. For details, see:
2264  				// https://discourse.llvm.org/t/rfc-enabling-wstrict-prototypes-by-default-in-c/60521
2265  				calleeType = llvm.FunctionType(callee.GlobalValueType().ReturnType(), nil, false)
2266  			}
2267  		case *ssa.MakeClosure:
2268  			// A call on a func value, but the callee is trivial to find. For
2269  			// example: immediately applied functions.
2270  			funcValue := b.getValue(value, getPos(value))
2271  			context = b.extractFuncContext(funcValue)
2272  		default:
2273  			panic("StaticCallee returned an unexpected value")
2274  		}
2275  		exported = info.exported || strings.HasPrefix(info.linkName, "llvm.")
2276  	} else if call, ok := instr.Value.(*ssa.Builtin); ok {
2277  		// Builtin function (append, close, delete, etc.).)
2278  		var argTypes []types.Type
2279  		for _, arg := range instr.Args {
2280  			argTypes = append(argTypes, arg.Type())
2281  		}
2282  		return b.createBuiltin(argTypes, params, call.Name(), instr.Pos())
2283  	} else if instr.IsInvoke() {
2284  		// Interface method call (aka invoke call).
2285  		itf := b.getValue(instr.Value, getPos(instr)) // interface value (runtime._interface)
2286  		typecode := b.CreateExtractValue(itf, 0, "invoke.func.typecode")
2287  		value := b.CreateExtractValue(itf, 1, "invoke.func.value") // receiver
2288  		// Prefix the params with receiver value and suffix with typecode.
2289  		params = append([]llvm.Value{value}, params...)
2290  		params = append(params, typecode)
2291  		callee = b.getInvokeFunction(instr)
2292  		calleeType = callee.GlobalValueType()
2293  		context = llvm.Undef(b.dataPtrType)
2294  	} else {
2295  		// Function pointer.
2296  		value := b.getValue(instr.Value, getPos(instr))
2297  		// This is a func value, which cannot be called directly. We have to
2298  		// extract the function pointer and context first from the func value.
2299  		callee, context = b.decodeFuncValue(value)
2300  		calleeType = b.getLLVMFunctionType(instr.Value.Type().Underlying().(*types.Signature))
2301  		b.createNilCheck(instr.Value, callee, "fpcall")
2302  	}
2303  
2304  	if !exported {
2305  		// This function takes a context parameter.
2306  		// Add it to the end of the parameter list.
2307  		params = append(params, context)
2308  	}
2309  
2310  	return b.createInvoke(calleeType, callee, params, ""), nil
2311  }
2312  
2313  // getValue returns the LLVM value of a constant, function value, global, or
2314  // already processed SSA expression.
2315  func (b *builder) getValue(expr ssa.Value, pos token.Pos) llvm.Value {
2316  	switch expr := expr.(type) {
2317  	case *ssa.Const:
2318  		if pos == token.NoPos {
2319  			file := b.program.Fset.File(b.fn.Pos())
2320  			if file != nil {
2321  				pos = file.Pos(0)
2322  			}
2323  		}
2324  		return b.createConst(expr, pos)
2325  	case *ssa.Function:
2326  		if b.getFunctionInfo(expr).exported {
2327  			b.addError(expr.Pos(), "cannot use an exported function as value: "+expr.String())
2328  			return llvm.Undef(b.getLLVMType(expr.Type()))
2329  		}
2330  		_, fn := b.getFunction(expr)
2331  		return b.createFuncValue(fn, llvm.ConstPointerNull(b.dataPtrType), expr.Signature)
2332  	case *ssa.Global:
2333  		value := b.getGlobal(expr)
2334  		if value.IsNil() {
2335  			b.addError(expr.Pos(), "global not found: "+expr.RelString(nil))
2336  			return llvm.Undef(b.getLLVMType(expr.Type()))
2337  		}
2338  		return value
2339  	default:
2340  		// other (local) SSA value
2341  		if value, ok := b.locals[expr]; ok {
2342  			return value
2343  		} else {
2344  			// indicates a compiler bug
2345  			panic("SSA value not previously found in function: " + expr.String())
2346  		}
2347  	}
2348  }
2349  
2350  // maxSliceSize determines the maximum size a slice of the given element type
2351  // can be.
2352  func (c *compilerContext) maxSliceSize(elementType llvm.Type) uint64 {
2353  	// Calculate ^uintptr(0), which is the max value that fits in uintptr.
2354  	maxPointerValue := llvm.ConstNot(llvm.ConstInt(c.uintptrType, 0, false)).ZExtValue()
2355  	// Calculate (^uint(0))/2, which is the max value that fits in an int.
2356  	maxIntegerValue := llvm.ConstNot(llvm.ConstInt(c.intType, 0, false)).ZExtValue() / 2
2357  
2358  	// Determine the maximum allowed size for a slice. The biggest possible
2359  	// pointer (starting from 0) would be maxPointerValue*sizeof(elementType) so
2360  	// divide by the element type to get the real maximum size.
2361  	elementSize := c.targetData.TypeAllocSize(elementType)
2362  	if elementSize == 0 {
2363  		elementSize = 1
2364  	}
2365  	maxSize := maxPointerValue / elementSize
2366  
2367  	// len(slice) is an int. Make sure the length remains small enough to fit in
2368  	// an int.
2369  	if maxSize > maxIntegerValue {
2370  		maxSize = maxIntegerValue
2371  	}
2372  
2373  	return maxSize
2374  }
2375  
2376  // createExpr translates a Go SSA expression to LLVM IR. This can be zero, one,
2377  // or multiple LLVM IR instructions and/or runtime calls.
2378  func (b *builder) createExpr(expr ssa.Value) (llvm.Value, error) {
2379  	if _, ok := b.locals[expr]; ok {
2380  		// sanity check
2381  		panic("instruction has already been created: " + expr.String())
2382  	}
2383  
2384  	switch expr := expr.(type) {
2385  	case *ssa.Alloc:
2386  		typ := b.getLLVMType(expr.Type().Underlying().(*types.Pointer).Elem())
2387  		size := b.targetData.TypeAllocSize(typ)
2388  		// Move all "large" allocations to the heap.
2389  		if expr.Heap || size > b.MaxStackAlloc {
2390  			// Calculate ^uintptr(0)
2391  			maxSize := llvm.ConstNot(llvm.ConstInt(b.uintptrType, 0, false)).ZExtValue()
2392  			if size > maxSize {
2393  				// Size would be truncated if truncated to uintptr.
2394  				return llvm.Value{}, b.makeError(expr.Pos(), fmt.Sprintf("value is too big (%v bytes)", size))
2395  			}
2396  			sizeValue := llvm.ConstInt(b.uintptrType, size, false)
2397  			layoutValue := b.createObjectLayout(typ, expr.Pos())
2398  			buf := b.createRuntimeCall("alloc", []llvm.Value{sizeValue, layoutValue}, expr.Comment)
2399  			align := b.targetData.ABITypeAlignment(typ)
2400  			buf.AddCallSiteAttribute(0, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(align)))
2401  			if b.PrintAllocs != nil {
2402  				b.emitLogAlloc(expr.Pos())
2403  			}
2404  	
2405  			return buf, nil
2406  		} else {
2407  			buf := llvmutil.CreateEntryBlockAlloca(b.Builder, typ, expr.Comment)
2408  			if b.targetData.TypeAllocSize(typ) != 0 {
2409  				b.CreateStore(llvm.ConstNull(typ), buf)
2410  			}
2411  			return buf, nil
2412  		}
2413  	case *ssa.BinOp:
2414  		x := b.getValue(expr.X, getPos(expr))
2415  		y := b.getValue(expr.Y, getPos(expr))
2416  		return b.createBinOp(expr.Op, expr.X.Type(), expr.Y.Type(), x, y, expr.Pos())
2417  	case *ssa.Call:
2418  		return b.createFunctionCall(expr.Common(), expr)
2419  	case *ssa.ChangeInterface:
2420  		// Do not change between interface types: always use the underlying
2421  		// (concrete) type in the type number of the interface. Every method
2422  		// call on an interface will do a lookup which method to call.
2423  		// This is different from how the official Go compiler works, because of
2424  		// heap allocation and because it's easier to implement, see:
2425  		// https://research.swtch.com/interfaces
2426  		return b.getValue(expr.X, getPos(expr)), nil
2427  	case *ssa.ChangeType:
2428  		// This instruction changes the type, but the underlying value remains
2429  		// the same. This is often a no-op, but sometimes we have to change the
2430  		// LLVM type as well.
2431  		x := b.getValue(expr.X, getPos(expr))
2432  		llvmType := b.getLLVMType(expr.Type())
2433  		if x.Type() == llvmType {
2434  			// Different Go type but same LLVM type (for example, named int).
2435  			// This is the common case.
2436  			return x, nil
2437  		}
2438  		// Figure out what kind of type we need to cast.
2439  		switch llvmType.TypeKind() {
2440  		case llvm.StructTypeKind:
2441  			// Unfortunately, we can't just bitcast structs. We have to
2442  			// actually create a new struct of the correct type and insert the
2443  			// values from the previous struct in there.
2444  			value := llvm.Undef(llvmType)
2445  			for i := 0; i < llvmType.StructElementTypesCount(); i++ {
2446  				field := b.CreateExtractValue(x, i, "changetype.field")
2447  				value = b.CreateInsertValue(value, field, i, "changetype.struct")
2448  			}
2449  			return value, nil
2450  		default:
2451  			return llvm.Value{}, errors.New("todo: unknown ChangeType type: " + expr.X.Type().String())
2452  		}
2453  	case *ssa.Const:
2454  		panic("const is not an expression")
2455  	case *ssa.Convert:
2456  		x := b.getValue(expr.X, getPos(expr))
2457  		return b.createConvert(expr.X.Type(), expr.Type(), x, expr.Pos())
2458  	case *ssa.Extract:
2459  		if _, ok := expr.Tuple.(*ssa.Select); ok {
2460  			return b.getChanSelectResult(expr), nil
2461  		}
2462  		value := b.getValue(expr.Tuple, getPos(expr))
2463  		return b.CreateExtractValue(value, expr.Index, ""), nil
2464  	case *ssa.Field:
2465  		value := b.getValue(expr.X, getPos(expr))
2466  		result := b.CreateExtractValue(value, expr.Field, "")
2467  		return result, nil
2468  	case *ssa.FieldAddr:
2469  		val := b.getValue(expr.X, getPos(expr))
2470  		// Check for nil pointer before calculating the address, from the spec:
2471  		// > For an operand x of type T, the address operation &x generates a
2472  		// > pointer of type *T to x. [...] If the evaluation of x would cause a
2473  		// > run-time panic, then the evaluation of &x does too.
2474  		b.createNilCheck(expr.X, val, "gep")
2475  		// Do a GEP on the pointer to get the field address.
2476  		indices := []llvm.Value{
2477  			llvm.ConstInt(b.ctx.Int32Type(), 0, false),
2478  			llvm.ConstInt(b.ctx.Int32Type(), uint64(expr.Field), false),
2479  		}
2480  		elementType := b.getLLVMType(expr.X.Type().Underlying().(*types.Pointer).Elem())
2481  		return b.CreateInBoundsGEP(elementType, val, indices, ""), nil
2482  	case *ssa.Function:
2483  		panic("function is not an expression")
2484  	case *ssa.Global:
2485  		panic("global is not an expression")
2486  	case *ssa.Index:
2487  		collection := b.getValue(expr.X, getPos(expr))
2488  		index := b.getValue(expr.Index, getPos(expr))
2489  
2490  		switch xType := expr.X.Type().Underlying().(type) {
2491  		case *types.Basic: // extract byte from string
2492  			// Value type must be a string, which is a basic type.
2493  			if xType.Info()&types.IsString == 0 {
2494  				panic("lookup on non-string?")
2495  			}
2496  
2497  			// Sometimes, the index can be e.g. an uint8 or int8, and we have to
2498  			// correctly extend that type for two reasons:
2499  			//  1. The lookup bounds check expects an index of at least uintptr
2500  			//     size.
2501  			//  2. getelementptr has signed operands, and therefore s[uint8(x)]
2502  			//     can be lowered as s[int8(x)]. That would be a bug.
2503  			index = b.extendInteger(index, expr.Index.Type(), b.uintptrType)
2504  
2505  			// Bounds check.
2506  			length := b.CreateExtractValue(collection, 1, "len")
2507  			b.createLookupBoundsCheck(length, index)
2508  
2509  			// Lookup byte
2510  			buf := b.CreateExtractValue(collection, 0, "")
2511  			bufElemType := b.ctx.Int8Type()
2512  			bufPtr := b.CreateInBoundsGEP(bufElemType, buf, []llvm.Value{index}, "")
2513  			return b.CreateLoad(bufElemType, bufPtr, ""), nil
2514  		case *types.Slice: // Moxie: []byte index (string=[]byte unification)
2515  			if basic, ok := xType.Elem().(*types.Basic); ok && basic.Kind() == types.Byte {
2516  				index = b.extendInteger(index, expr.Index.Type(), b.uintptrType)
2517  				length := b.CreateExtractValue(collection, 1, "len")
2518  				b.createLookupBoundsCheck(length, index)
2519  				buf := b.CreateExtractValue(collection, 0, "")
2520  				bufElemType := b.ctx.Int8Type()
2521  				bufPtr := b.CreateInBoundsGEP(bufElemType, buf, []llvm.Value{index}, "")
2522  				return b.CreateLoad(bufElemType, bufPtr, ""), nil
2523  			}
2524  			panic("unexpected slice type in *ssa.Index")
2525  		case *types.Array: // extract element from array
2526  			// Extend index to at least uintptr size, because getelementptr
2527  			// assumes index is a signed integer.
2528  			index = b.extendInteger(index, expr.Index.Type(), b.uintptrType)
2529  
2530  			// Check bounds.
2531  			arrayLen := llvm.ConstInt(b.uintptrType, uint64(xType.Len()), false)
2532  			b.createLookupBoundsCheck(arrayLen, index)
2533  
2534  			// Can't load directly from array (as index is non-constant), so
2535  			// have to do it using an alloca+gep+load.
2536  			arrayType := collection.Type()
2537  			alloca, allocaSize := b.createTemporaryAlloca(arrayType, "index.alloca")
2538  			b.CreateStore(collection, alloca)
2539  			zero := llvm.ConstInt(b.ctx.Int32Type(), 0, false)
2540  			ptr := b.CreateInBoundsGEP(arrayType, alloca, []llvm.Value{zero, index}, "index.gep")
2541  			result := b.CreateLoad(arrayType.ElementType(), ptr, "index.load")
2542  			b.emitLifetimeEnd(alloca, allocaSize)
2543  			return result, nil
2544  		default:
2545  			panic("unknown *ssa.Index type")
2546  		}
2547  	case *ssa.IndexAddr:
2548  		val := b.getValue(expr.X, getPos(expr))
2549  		index := b.getValue(expr.Index, getPos(expr))
2550  
2551  		// Get buffer pointer and length
2552  		var bufptr, buflen llvm.Value
2553  		var bufType llvm.Type
2554  		switch ptrTyp := expr.X.Type().Underlying().(type) {
2555  		case *types.Pointer:
2556  			typ := ptrTyp.Elem().Underlying()
2557  			switch typ := typ.(type) {
2558  			case *types.Array:
2559  				bufptr = val
2560  				buflen = llvm.ConstInt(b.uintptrType, uint64(typ.Len()), false)
2561  				bufType = b.getLLVMType(typ)
2562  				// Check for nil pointer before calculating the address, from
2563  				// the spec:
2564  				// > For an operand x of type T, the address operation &x
2565  				// > generates a pointer of type *T to x. [...] If the
2566  				// > evaluation of x would cause a run-time panic, then the
2567  				// > evaluation of &x does too.
2568  				b.createNilCheck(expr.X, bufptr, "gep")
2569  			default:
2570  				return llvm.Value{}, b.makeError(expr.Pos(), "todo: indexaddr: "+typ.String())
2571  			}
2572  		case *types.Slice:
2573  			bufptr = b.CreateExtractValue(val, 0, "indexaddr.ptr")
2574  			buflen = b.CreateExtractValue(val, 1, "indexaddr.len")
2575  			bufType = b.getLLVMType(ptrTyp.Elem())
2576  		case *types.Basic: // Moxie: string=[]byte, addressable indexing
2577  			bufptr = b.CreateExtractValue(val, 0, "indexaddr.ptr")
2578  			buflen = b.CreateExtractValue(val, 1, "indexaddr.len")
2579  			bufType = b.ctx.Int8Type()
2580  		default:
2581  			return llvm.Value{}, b.makeError(expr.Pos(), "todo: indexaddr: "+ptrTyp.String())
2582  		}
2583  
2584  		// Make sure index is at least the size of uintptr because getelementptr
2585  		// assumes index is a signed integer.
2586  		index = b.extendInteger(index, expr.Index.Type(), b.uintptrType)
2587  
2588  		// Bounds check.
2589  		b.createLookupBoundsCheck(buflen, index)
2590  
2591  		switch expr.X.Type().Underlying().(type) {
2592  		case *types.Pointer:
2593  			indices := []llvm.Value{
2594  				llvm.ConstInt(b.ctx.Int32Type(), 0, false),
2595  				index,
2596  			}
2597  			return b.CreateInBoundsGEP(bufType, bufptr, indices, ""), nil
2598  		case *types.Slice:
2599  			return b.CreateInBoundsGEP(bufType, bufptr, []llvm.Value{index}, ""), nil
2600  		case *types.Basic: // Moxie: string=[]byte, addressable
2601  			return b.CreateInBoundsGEP(bufType, bufptr, []llvm.Value{index}, ""), nil
2602  		default:
2603  			panic("unreachable")
2604  		}
2605  	case *ssa.Lookup: // map lookup
2606  		value := b.getValue(expr.X, getPos(expr))
2607  		index := b.getValue(expr.Index, getPos(expr))
2608  		valueType := expr.Type()
2609  		if expr.CommaOk {
2610  			valueType = valueType.(*types.Tuple).At(0).Type()
2611  		}
2612  		return b.createMapLookup(expr.X.Type().Underlying().(*types.Map).Key(), valueType, value, index, expr.CommaOk, expr.Pos())
2613  	case *ssa.MakeChan:
2614  		return b.createMakeChan(expr), nil
2615  	case *ssa.MakeClosure:
2616  		return b.parseMakeClosure(expr)
2617  	case *ssa.MakeInterface:
2618  		val := b.getValue(expr.X, getPos(expr))
2619  		return b.createMakeInterface(val, expr.X.Type(), expr.Pos()), nil
2620  	case *ssa.MakeMap:
2621  		return b.createMakeMap(expr)
2622  	case *ssa.MakeSlice:
2623  		sliceLen := b.getValue(expr.Len, getPos(expr))
2624  		sliceCap := b.getValue(expr.Cap, getPos(expr))
2625  		sliceType := expr.Type().Underlying().(*types.Slice)
2626  		llvmElemType := b.getLLVMType(sliceType.Elem())
2627  		elemSize := b.targetData.TypeAllocSize(llvmElemType)
2628  		elemAlign := b.targetData.ABITypeAlignment(llvmElemType)
2629  		elemSizeValue := llvm.ConstInt(b.uintptrType, elemSize, false)
2630  
2631  		maxSize := b.maxSliceSize(llvmElemType)
2632  		if elemSize > maxSize {
2633  			// This seems to be checked by the typechecker already, but let's
2634  			// check it again just to be sure.
2635  			return llvm.Value{}, b.makeError(expr.Pos(), fmt.Sprintf("slice element type is too big (%v bytes)", elemSize))
2636  		}
2637  
2638  		// Bounds checking.
2639  		lenType := expr.Len.Type().Underlying().(*types.Basic)
2640  		capType := expr.Cap.Type().Underlying().(*types.Basic)
2641  		maxSizeValue := llvm.ConstInt(b.uintptrType, maxSize, false)
2642  		b.createSliceBoundsCheck(maxSizeValue, sliceLen, sliceCap, sliceCap, lenType, capType, capType)
2643  
2644  		// Allocate the backing array.
2645  		sliceCapCast, err := b.createConvert(expr.Cap.Type(), types.Typ[types.Uintptr], sliceCap, expr.Pos())
2646  		if err != nil {
2647  			return llvm.Value{}, err
2648  		}
2649  		sliceSize := b.CreateBinOp(llvm.Mul, elemSizeValue, sliceCapCast, "makeslice.cap")
2650  		layoutValue := b.createObjectLayout(llvmElemType, expr.Pos())
2651  		slicePtr := b.createRuntimeCall("alloc", []llvm.Value{sliceSize, layoutValue}, "makeslice.buf")
2652  		slicePtr.AddCallSiteAttribute(0, b.ctx.CreateEnumAttribute(llvm.AttributeKindID("align"), uint64(elemAlign)))
2653  		if b.PrintAllocs != nil {
2654  			b.emitLogAlloc(expr.Pos())
2655  		}
2656  	
2657  
2658  		// Extend or truncate if necessary. This is safe as we've already done
2659  		// the bounds check.
2660  		sliceLen, err = b.createConvert(expr.Len.Type(), types.Typ[types.Uintptr], sliceLen, expr.Pos())
2661  		if err != nil {
2662  			return llvm.Value{}, err
2663  		}
2664  		sliceCap, err = b.createConvert(expr.Cap.Type(), types.Typ[types.Uintptr], sliceCap, expr.Pos())
2665  		if err != nil {
2666  			return llvm.Value{}, err
2667  		}
2668  
2669  		// Create the slice.
2670  		// Moxie: use getLLVMType to get named type for []byte.
2671  		slice := llvm.Undef(b.getLLVMType(sliceType))
2672  		slice = b.CreateInsertValue(slice, slicePtr, 0, "")
2673  		slice = b.CreateInsertValue(slice, sliceLen, 1, "")
2674  		slice = b.CreateInsertValue(slice, sliceCap, 2, "")
2675  		return slice, nil
2676  	case *ssa.Next:
2677  		rangeVal := expr.Iter.(*ssa.Range).X
2678  		llvmRangeVal := b.getValue(rangeVal, getPos(expr))
2679  		it := b.getValue(expr.Iter, getPos(expr))
2680  		if expr.IsString {
2681  			return b.createRuntimeCall("stringNext", []llvm.Value{llvmRangeVal, it}, "range.next"), nil
2682  		} else { // map
2683  			return b.createMapIteratorNext(rangeVal, llvmRangeVal, it), nil
2684  		}
2685  	case *ssa.Phi:
2686  		phi := b.CreatePHI(b.getLLVMType(expr.Type()), "")
2687  		b.phis = append(b.phis, phiNode{expr, phi})
2688  		return phi, nil
2689  	case *ssa.Range:
2690  		var iteratorType llvm.Type
2691  		switch typ := expr.X.Type().Underlying().(type) {
2692  		case *types.Basic: // string range (yields runes via UTF-8 decode)
2693  			iteratorType = b.getLLVMRuntimeType("stringIterator")
2694  		case *types.Map:
2695  			iteratorType = b.getLLVMRuntimeType("hashmapIterator")
2696  		default:
2697  			panic("unknown type in range: " + typ.String())
2698  		}
2699  		it, _ := b.createTemporaryAlloca(iteratorType, "range.it")
2700  		b.CreateStore(llvm.ConstNull(iteratorType), it)
2701  		return it, nil
2702  	case *ssa.Select:
2703  		return b.createSelect(expr), nil
2704  	case *ssa.Slice:
2705  		value := b.getValue(expr.X, getPos(expr))
2706  
2707  		var lowType, highType, maxType *types.Basic
2708  		var low, high, max llvm.Value
2709  
2710  		if expr.Low != nil {
2711  			lowType = expr.Low.Type().Underlying().(*types.Basic)
2712  			low = b.getValue(expr.Low, getPos(expr))
2713  			low = b.extendInteger(low, lowType, b.uintptrType)
2714  		} else {
2715  			lowType = types.Typ[types.Uintptr]
2716  			low = llvm.ConstInt(b.uintptrType, 0, false)
2717  		}
2718  
2719  		if expr.High != nil {
2720  			highType = expr.High.Type().Underlying().(*types.Basic)
2721  			high = b.getValue(expr.High, getPos(expr))
2722  			high = b.extendInteger(high, highType, b.uintptrType)
2723  		} else {
2724  			highType = types.Typ[types.Uintptr]
2725  		}
2726  
2727  		if expr.Max != nil {
2728  			maxType = expr.Max.Type().Underlying().(*types.Basic)
2729  			max = b.getValue(expr.Max, getPos(expr))
2730  			max = b.extendInteger(max, maxType, b.uintptrType)
2731  		} else {
2732  			maxType = types.Typ[types.Uintptr]
2733  		}
2734  
2735  		switch typ := expr.X.Type().Underlying().(type) {
2736  		case *types.Pointer: // pointer to array
2737  			// slice an array
2738  			arrayType := typ.Elem().Underlying().(*types.Array)
2739  			length := arrayType.Len()
2740  			llvmLen := llvm.ConstInt(b.uintptrType, uint64(length), false)
2741  			if high.IsNil() {
2742  				high = llvmLen
2743  			}
2744  			if max.IsNil() {
2745  				max = llvmLen
2746  			}
2747  			indices := []llvm.Value{
2748  				llvm.ConstInt(b.ctx.Int32Type(), 0, false),
2749  				low,
2750  			}
2751  
2752  			b.createNilCheck(expr.X, value, "slice")
2753  			b.createSliceBoundsCheck(llvmLen, low, high, max, lowType, highType, maxType)
2754  
2755  			// Truncate ints bigger than uintptr. This is after the bounds
2756  			// check so it's safe.
2757  			if b.targetData.TypeAllocSize(low.Type()) > b.targetData.TypeAllocSize(b.uintptrType) {
2758  				low = b.CreateTrunc(low, b.uintptrType, "")
2759  			}
2760  			if b.targetData.TypeAllocSize(high.Type()) > b.targetData.TypeAllocSize(b.uintptrType) {
2761  				high = b.CreateTrunc(high, b.uintptrType, "")
2762  			}
2763  			if b.targetData.TypeAllocSize(max.Type()) > b.targetData.TypeAllocSize(b.uintptrType) {
2764  				max = b.CreateTrunc(max, b.uintptrType, "")
2765  			}
2766  
2767  			sliceLen := b.CreateSub(high, low, "slice.len")
2768  			srcPtr := b.CreateInBoundsGEP(b.getLLVMType(arrayType), value, indices, "slice.ptr")
2769  
2770  			// Moxie: subslice copies to break aliasing.
2771  			ptrElemType := b.getLLVMType(arrayType.Elem())
2772  			elemSize := b.targetData.TypeAllocSize(ptrElemType)
2773  			elemSizeVal := llvm.ConstInt(b.uintptrType, elemSize, false)
2774  			copySize := b.CreateMul(sliceLen, elemSizeVal, "slice.copy.size")
2775  			layoutValue := b.createObjectLayout(ptrElemType, expr.Pos())
2776  			newPtr := b.createRuntimeCall("alloc", []llvm.Value{copySize, layoutValue}, "slice.copy.buf")
2777  			b.createRuntimeCall("memcpy", []llvm.Value{newPtr, srcPtr, copySize}, "")
2778  
2779  			slice := llvm.Undef(b.getLLVMType(expr.Type()))
2780  			slice = b.CreateInsertValue(slice, newPtr, 0, "")
2781  			slice = b.CreateInsertValue(slice, sliceLen, 1, "")
2782  			slice = b.CreateInsertValue(slice, sliceLen, 2, "") // cap = len for copies
2783  			return slice, nil
2784  
2785  		case *types.Slice:
2786  			// slice a slice
2787  			oldPtr := b.CreateExtractValue(value, 0, "")
2788  			oldLen := b.CreateExtractValue(value, 1, "")
2789  			oldCap := b.CreateExtractValue(value, 2, "")
2790  			if high.IsNil() {
2791  				high = oldLen
2792  			}
2793  			if max.IsNil() {
2794  				max = oldCap
2795  			}
2796  
2797  			b.createSliceBoundsCheck(oldCap, low, high, max, lowType, highType, maxType)
2798  
2799  			if b.targetData.TypeAllocSize(low.Type()) > b.targetData.TypeAllocSize(b.uintptrType) {
2800  				low = b.CreateTrunc(low, b.uintptrType, "")
2801  			}
2802  			if b.targetData.TypeAllocSize(high.Type()) > b.targetData.TypeAllocSize(b.uintptrType) {
2803  				high = b.CreateTrunc(high, b.uintptrType, "")
2804  			}
2805  			if b.targetData.TypeAllocSize(max.Type()) > b.targetData.TypeAllocSize(b.uintptrType) {
2806  				max = b.CreateTrunc(max, b.uintptrType, "")
2807  			}
2808  
2809  			ptrElemType := b.getLLVMType(typ.Elem())
2810  			newLen := b.CreateSub(high, low, "")
2811  
2812  			if expr.Low == nil {
2813  				// s[:high] - reslice with implicit low=0.
2814  				// Same base pointer, no aliasing, preserve cap.
2815  				newCap := b.CreateSub(max, low, "")
2816  				slice := llvm.Undef(b.getLLVMType(typ))
2817  				slice = b.CreateInsertValue(slice, oldPtr, 0, "")
2818  				slice = b.CreateInsertValue(slice, newLen, 1, "")
2819  				slice = b.CreateInsertValue(slice, newCap, 2, "")
2820  				return slice, nil
2821  			}
2822  
2823  			// s[low:high] - Moxie: copy to break aliasing.
2824  			srcPtr := b.CreateInBoundsGEP(ptrElemType, oldPtr, []llvm.Value{low}, "")
2825  			elemSize := b.targetData.TypeAllocSize(ptrElemType)
2826  			elemSizeVal := llvm.ConstInt(b.uintptrType, elemSize, false)
2827  			copySize := b.CreateMul(newLen, elemSizeVal, "slice.copy.size")
2828  			layoutValue := b.createObjectLayout(ptrElemType, expr.Pos())
2829  			newPtr := b.createRuntimeCall("alloc", []llvm.Value{copySize, layoutValue}, "slice.copy.buf")
2830  			b.createRuntimeCall("memcpy", []llvm.Value{newPtr, srcPtr, copySize}, "")
2831  			slice := llvm.Undef(b.getLLVMType(typ))
2832  			slice = b.CreateInsertValue(slice, newPtr, 0, "")
2833  			slice = b.CreateInsertValue(slice, newLen, 1, "")
2834  			slice = b.CreateInsertValue(slice, newLen, 2, "") // cap = len for copies
2835  			return slice, nil
2836  
2837  		case *types.Basic:
2838  			if typ.Info()&types.IsString == 0 {
2839  				return llvm.Value{}, b.makeError(expr.Pos(), "unknown slice type: "+typ.String())
2840  			}
2841  			// slice a string
2842  			if expr.Max != nil {
2843  				// This might as well be a panic, as the frontend should have
2844  				// handled this already.
2845  				return llvm.Value{}, b.makeError(expr.Pos(), "slicing a string with a max parameter is not allowed by the spec")
2846  			}
2847  			oldPtr := b.CreateExtractValue(value, 0, "")
2848  			oldLen := b.CreateExtractValue(value, 1, "")
2849  			if high.IsNil() {
2850  				high = oldLen
2851  			}
2852  
2853  			b.createSliceBoundsCheck(oldLen, low, high, high, lowType, highType, maxType)
2854  
2855  			// Truncate ints bigger than uintptr. This is after the bounds
2856  			// check so it's safe.
2857  			if b.targetData.TypeAllocSize(low.Type()) > b.targetData.TypeAllocSize(b.uintptrType) {
2858  				low = b.CreateTrunc(low, b.uintptrType, "")
2859  			}
2860  			if b.targetData.TypeAllocSize(high.Type()) > b.targetData.TypeAllocSize(b.uintptrType) {
2861  				high = b.CreateTrunc(high, b.uintptrType, "")
2862  			}
2863  
2864  			srcPtr := b.CreateInBoundsGEP(b.ctx.Int8Type(), oldPtr, []llvm.Value{low}, "")
2865  			newLen := b.CreateSub(high, low, "")
2866  			// Moxie: subslice copies to break aliasing.
2867  			layoutValue := llvm.ConstNull(b.dataPtrType)
2868  			newPtr := b.createRuntimeCall("alloc", []llvm.Value{newLen, layoutValue}, "str.copy.buf")
2869  			b.createRuntimeCall("memcpy", []llvm.Value{newPtr, srcPtr, newLen}, "")
2870  			str := llvm.Undef(b.getLLVMRuntimeType("_string"))
2871  			str = b.CreateInsertValue(str, newPtr, 0, "")
2872  			str = b.CreateInsertValue(str, newLen, 1, "")
2873  			str = b.CreateInsertValue(str, newLen, 2, "") // cap = len for copies
2874  			return str, nil
2875  
2876  		default:
2877  			return llvm.Value{}, b.makeError(expr.Pos(), "unknown slice type: "+typ.String())
2878  		}
2879  	case *ssa.SliceToArrayPointer:
2880  		// Conversion from a slice to an array pointer, as the name clearly
2881  		// says. This requires a runtime check to make sure the slice is at
2882  		// least as big as the array.
2883  		slice := b.getValue(expr.X, getPos(expr))
2884  		sliceLen := b.CreateExtractValue(slice, 1, "")
2885  		arrayLen := expr.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Array).Len()
2886  		b.createSliceToArrayPointerCheck(sliceLen, arrayLen)
2887  		ptr := b.CreateExtractValue(slice, 0, "")
2888  		return ptr, nil
2889  	case *ssa.TypeAssert:
2890  		return b.createTypeAssert(expr), nil
2891  	case *ssa.UnOp:
2892  		return b.createUnOp(expr)
2893  	default:
2894  		return llvm.Value{}, b.makeError(expr.Pos(), "todo: unknown expression: "+expr.String())
2895  	}
2896  }
2897  
2898  // createBinOp creates a LLVM binary operation (add, sub, mul, etc) for a Go
2899  // binary operation. This is almost a direct mapping, but there are some subtle
2900  // differences such as the requirement in LLVM IR that both sides must have the
2901  // same type, even for bitshifts. Also, signedness in Go is encoded in the type
2902  // and is encoded in the operation in LLVM IR: this is important for some
2903  // operations such as divide.
2904  func (b *builder) createBinOp(op token.Token, typ, ytyp types.Type, x, y llvm.Value, pos token.Pos) (llvm.Value, error) {
2905  	switch typ := typ.Underlying().(type) {
2906  	case *types.Basic:
2907  		if typ.Info()&types.IsInteger != 0 {
2908  			// Operations on integers
2909  			signed := typ.Info()&types.IsUnsigned == 0
2910  			switch op {
2911  			case token.ADD: // +
2912  				return b.CreateAdd(x, y, ""), nil
2913  			case token.SUB: // -
2914  				return b.CreateSub(x, y, ""), nil
2915  			case token.MUL: // *
2916  				return b.CreateMul(x, y, ""), nil
2917  			case token.QUO, token.REM: // /, %
2918  				// Check for a divide by zero. If y is zero, the Go
2919  				// specification says that a runtime error must be triggered.
2920  				b.createDivideByZeroCheck(y)
2921  
2922  				if signed {
2923  					// Deal with signed division overflow.
2924  					// The LLVM LangRef says:
2925  					//
2926  					//   Overflow also leads to undefined behavior; this is a
2927  					//   rare case, but can occur, for example, by doing a
2928  					//   32-bit division of -2147483648 by -1.
2929  					//
2930  					// The Go specification however says this about division:
2931  					//
2932  					//   The one exception to this rule is that if the dividend
2933  					//   x is the most negative value for the int type of x, the
2934  					//   quotient q = x / -1 is equal to x (and r = 0) due to
2935  					//   two's-complement integer overflow.
2936  					//
2937  					// In other words, in the special case that the lowest
2938  					// possible signed integer is divided by -1, the result of
2939  					// the division is the same as x (the dividend).
2940  					// This is implemented by checking for this condition and
2941  					// changing y to 1 if it occurs, for example for 32-bit
2942  					// ints:
2943  					//
2944  					//   if x == -2147483648 && y == -1 {
2945  					//       y = 1
2946  					//   }
2947  					//
2948  					// Dividing x by 1 obviously returns x, therefore satisfying
2949  					// the Go specification without a branch.
2950  					llvmType := x.Type()
2951  					minusOne := llvm.ConstSub(llvm.ConstInt(llvmType, 0, false), llvm.ConstInt(llvmType, 1, false))
2952  					lowestInteger := llvm.ConstInt(x.Type(), 1<<(llvmType.IntTypeWidth()-1), false)
2953  					yIsMinusOne := b.CreateICmp(llvm.IntEQ, y, minusOne, "")
2954  					xIsLowestInteger := b.CreateICmp(llvm.IntEQ, x, lowestInteger, "")
2955  					hasOverflow := b.CreateAnd(yIsMinusOne, xIsLowestInteger, "")
2956  					y = b.CreateSelect(hasOverflow, llvm.ConstInt(llvmType, 1, true), y, "")
2957  
2958  					if op == token.QUO {
2959  						return b.CreateSDiv(x, y, ""), nil
2960  					} else {
2961  						return b.CreateSRem(x, y, ""), nil
2962  					}
2963  				} else {
2964  					if op == token.QUO {
2965  						return b.CreateUDiv(x, y, ""), nil
2966  					} else {
2967  						return b.CreateURem(x, y, ""), nil
2968  					}
2969  				}
2970  			case token.AND: // &
2971  				return b.CreateAnd(x, y, ""), nil
2972  			case token.OR: // |
2973  				return b.CreateOr(x, y, ""), nil
2974  			case token.XOR: // ^
2975  				return b.CreateXor(x, y, ""), nil
2976  			case token.SHL, token.SHR:
2977  				if ytyp.Underlying().(*types.Basic).Info()&types.IsUnsigned == 0 {
2978  					// Ensure that y is not negative.
2979  					b.createNegativeShiftCheck(y)
2980  				}
2981  
2982  				sizeX := b.targetData.TypeAllocSize(x.Type())
2983  				sizeY := b.targetData.TypeAllocSize(y.Type())
2984  
2985  				// Check if the shift is bigger than the bit-width of the shifted value.
2986  				// This is UB in LLVM, so it needs to be handled separately.
2987  				// The Go spec indirectly defines the result as 0.
2988  				// Negative shifts are handled earlier, so we can treat y as unsigned.
2989  				overshifted := b.CreateICmp(llvm.IntUGE, y, llvm.ConstInt(y.Type(), 8*sizeX, false), "shift.overflow")
2990  
2991  				// Adjust the size of y to match x.
2992  				switch {
2993  				case sizeX > sizeY:
2994  					y = b.CreateZExt(y, x.Type(), "")
2995  				case sizeX < sizeY:
2996  					// If it gets truncated, overshifted will be true and it will not matter.
2997  					y = b.CreateTrunc(y, x.Type(), "")
2998  				}
2999  
3000  				// Create a shift operation.
3001  				var val llvm.Value
3002  				switch op {
3003  				case token.SHL: // <<
3004  					val = b.CreateShl(x, y, "")
3005  				case token.SHR: // >>
3006  					if signed {
3007  						// Arithmetic right shifts work differently, since shifting a negative number right yields -1.
3008  						// Cap the shift input rather than selecting the output.
3009  						y = b.CreateSelect(overshifted, llvm.ConstInt(y.Type(), 8*sizeX-1, false), y, "shift.offset")
3010  						return b.CreateAShr(x, y, ""), nil
3011  					} else {
3012  						val = b.CreateLShr(x, y, "")
3013  					}
3014  				default:
3015  					panic("unreachable")
3016  				}
3017  
3018  				// Select between the shift result and zero depending on whether there was an overshift.
3019  				return b.CreateSelect(overshifted, llvm.ConstInt(val.Type(), 0, false), val, "shift.result"), nil
3020  			case token.EQL: // ==
3021  				return b.CreateICmp(llvm.IntEQ, x, y, ""), nil
3022  			case token.NEQ: // !=
3023  				return b.CreateICmp(llvm.IntNE, x, y, ""), nil
3024  			case token.AND_NOT: // &^
3025  				// Go specific. Calculate "and not" with x & (~y)
3026  				inv := b.CreateNot(y, "") // ~y
3027  				return b.CreateAnd(x, inv, ""), nil
3028  			case token.LSS: // <
3029  				if signed {
3030  					return b.CreateICmp(llvm.IntSLT, x, y, ""), nil
3031  				} else {
3032  					return b.CreateICmp(llvm.IntULT, x, y, ""), nil
3033  				}
3034  			case token.LEQ: // <=
3035  				if signed {
3036  					return b.CreateICmp(llvm.IntSLE, x, y, ""), nil
3037  				} else {
3038  					return b.CreateICmp(llvm.IntULE, x, y, ""), nil
3039  				}
3040  			case token.GTR: // >
3041  				if signed {
3042  					return b.CreateICmp(llvm.IntSGT, x, y, ""), nil
3043  				} else {
3044  					return b.CreateICmp(llvm.IntUGT, x, y, ""), nil
3045  				}
3046  			case token.GEQ: // >=
3047  				if signed {
3048  					return b.CreateICmp(llvm.IntSGE, x, y, ""), nil
3049  				} else {
3050  					return b.CreateICmp(llvm.IntUGE, x, y, ""), nil
3051  				}
3052  			default:
3053  				panic("binop on integer: " + op.String())
3054  			}
3055  		} else if typ.Info()&types.IsFloat != 0 {
3056  			// Operations on floats
3057  			switch op {
3058  			case token.ADD: // +
3059  				return b.CreateFAdd(x, y, ""), nil
3060  			case token.SUB: // -
3061  				return b.CreateFSub(x, y, ""), nil
3062  			case token.MUL: // *
3063  				return b.CreateFMul(x, y, ""), nil
3064  			case token.QUO: // /
3065  				return b.CreateFDiv(x, y, ""), nil
3066  			case token.EQL: // ==
3067  				return b.CreateFCmp(llvm.FloatOEQ, x, y, ""), nil
3068  			case token.NEQ: // !=
3069  				return b.CreateFCmp(llvm.FloatUNE, x, y, ""), nil
3070  			case token.LSS: // <
3071  				return b.CreateFCmp(llvm.FloatOLT, x, y, ""), nil
3072  			case token.LEQ: // <=
3073  				return b.CreateFCmp(llvm.FloatOLE, x, y, ""), nil
3074  			case token.GTR: // >
3075  				return b.CreateFCmp(llvm.FloatOGT, x, y, ""), nil
3076  			case token.GEQ: // >=
3077  				return b.CreateFCmp(llvm.FloatOGE, x, y, ""), nil
3078  			default:
3079  				panic("binop on float: " + op.String())
3080  			}
3081  		} else if typ.Info()&types.IsComplex != 0 {
3082  			r1 := b.CreateExtractValue(x, 0, "r1")
3083  			r2 := b.CreateExtractValue(y, 0, "r2")
3084  			i1 := b.CreateExtractValue(x, 1, "i1")
3085  			i2 := b.CreateExtractValue(y, 1, "i2")
3086  			switch op {
3087  			case token.EQL: // ==
3088  				req := b.CreateFCmp(llvm.FloatOEQ, r1, r2, "")
3089  				ieq := b.CreateFCmp(llvm.FloatOEQ, i1, i2, "")
3090  				return b.CreateAnd(req, ieq, ""), nil
3091  			case token.NEQ: // !=
3092  				req := b.CreateFCmp(llvm.FloatOEQ, r1, r2, "")
3093  				ieq := b.CreateFCmp(llvm.FloatOEQ, i1, i2, "")
3094  				neq := b.CreateAnd(req, ieq, "")
3095  				return b.CreateNot(neq, ""), nil
3096  			case token.ADD, token.SUB:
3097  				var r, i llvm.Value
3098  				switch op {
3099  				case token.ADD:
3100  					r = b.CreateFAdd(r1, r2, "")
3101  					i = b.CreateFAdd(i1, i2, "")
3102  				case token.SUB:
3103  					r = b.CreateFSub(r1, r2, "")
3104  					i = b.CreateFSub(i1, i2, "")
3105  				default:
3106  					panic("unreachable")
3107  				}
3108  				cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{r.Type(), i.Type()}, false))
3109  				cplx = b.CreateInsertValue(cplx, r, 0, "")
3110  				cplx = b.CreateInsertValue(cplx, i, 1, "")
3111  				return cplx, nil
3112  			case token.MUL:
3113  				// Complex multiplication follows the current implementation in
3114  				// the Go compiler, with the difference that complex64
3115  				// components are not first scaled up to float64 for increased
3116  				// precision.
3117  				// https://github.com/golang/go/blob/170b8b4b12be50eeccbcdadb8523fb4fc670ca72/src/cmd/compile/internal/gc/ssa.go#L2089-L2127
3118  				// The implementation is as follows:
3119  				//   r := real(a) * real(b) - imag(a) * imag(b)
3120  				//   i := real(a) * imag(b) + imag(a) * real(b)
3121  				// Note: this does NOT follow the C11 specification (annex G):
3122  				// http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1548.pdf#page=549
3123  				// See https://github.com/golang/go/issues/29846 for a related
3124  				// discussion.
3125  				r := b.CreateFSub(b.CreateFMul(r1, r2, ""), b.CreateFMul(i1, i2, ""), "")
3126  				i := b.CreateFAdd(b.CreateFMul(r1, i2, ""), b.CreateFMul(i1, r2, ""), "")
3127  				cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{r.Type(), i.Type()}, false))
3128  				cplx = b.CreateInsertValue(cplx, r, 0, "")
3129  				cplx = b.CreateInsertValue(cplx, i, 1, "")
3130  				return cplx, nil
3131  			case token.QUO:
3132  				// Complex division.
3133  				// Do this in a library call because it's too difficult to do
3134  				// inline.
3135  				switch r1.Type().TypeKind() {
3136  				case llvm.FloatTypeKind:
3137  					return b.createRuntimeCall("complex64div", []llvm.Value{x, y}, ""), nil
3138  				case llvm.DoubleTypeKind:
3139  					return b.createRuntimeCall("complex128div", []llvm.Value{x, y}, ""), nil
3140  				default:
3141  					panic("unexpected complex type")
3142  				}
3143  			default:
3144  				panic("binop on complex: " + op.String())
3145  			}
3146  		} else if typ.Info()&types.IsBoolean != 0 {
3147  			// Operations on booleans
3148  			switch op {
3149  			case token.EQL: // ==
3150  				return b.CreateICmp(llvm.IntEQ, x, y, ""), nil
3151  			case token.NEQ: // !=
3152  				return b.CreateICmp(llvm.IntNE, x, y, ""), nil
3153  			default:
3154  				panic("binop on bool: " + op.String())
3155  			}
3156  		} else if typ.Kind() == types.UnsafePointer {
3157  			// Operations on pointers
3158  			switch op {
3159  			case token.EQL: // ==
3160  				return b.CreateICmp(llvm.IntEQ, x, y, ""), nil
3161  			case token.NEQ: // !=
3162  				return b.CreateICmp(llvm.IntNE, x, y, ""), nil
3163  			default:
3164  				panic("binop on pointer: " + op.String())
3165  			}
3166  		} else if typ.Info()&types.IsString != 0 {
3167  			// Operations on native string (exempt packages only).
3168  			// + is Go's string concat; | is Moxie's. Both lower to stringConcat.
3169  			switch op {
3170  			case token.ADD, token.OR:
3171  				return b.createRuntimeCall("stringConcat", []llvm.Value{x, y}, ""), nil
3172  			case token.EQL: // ==
3173  				return b.createRuntimeCall("stringEqual", []llvm.Value{x, y}, ""), nil
3174  			case token.NEQ: // !=
3175  				result := b.createRuntimeCall("stringEqual", []llvm.Value{x, y}, "")
3176  				return b.CreateNot(result, ""), nil
3177  			case token.LSS: // x < y
3178  				return b.createRuntimeCall("stringLess", []llvm.Value{x, y}, ""), nil
3179  			case token.LEQ: // x <= y becomes NOT (y < x)
3180  				result := b.createRuntimeCall("stringLess", []llvm.Value{y, x}, "")
3181  				return b.CreateNot(result, ""), nil
3182  			case token.GTR: // x > y becomes y < x
3183  				return b.createRuntimeCall("stringLess", []llvm.Value{y, x}, ""), nil
3184  			case token.GEQ: // x >= y becomes NOT (x < y)
3185  				result := b.createRuntimeCall("stringLess", []llvm.Value{x, y}, "")
3186  				return b.CreateNot(result, ""), nil
3187  			default:
3188  				panic("binop on string: " + op.String())
3189  			}
3190  		} else {
3191  			return llvm.Value{}, b.makeError(pos, "todo: unknown basic type in binop: "+typ.String())
3192  		}
3193  	case *types.Signature:
3194  		// Get raw scalars from the function value and compare those.
3195  		// Function values may be implemented in multiple ways, but they all
3196  		// have some way of getting a scalar value identifying the function.
3197  		// This is safe: function pointers are generally not comparable
3198  		// against each other, only against nil. So one of these has to be nil.
3199  		x = b.extractFuncScalar(x)
3200  		y = b.extractFuncScalar(y)
3201  		switch op {
3202  		case token.EQL: // ==
3203  			return b.CreateICmp(llvm.IntEQ, x, y, ""), nil
3204  		case token.NEQ: // !=
3205  			return b.CreateICmp(llvm.IntNE, x, y, ""), nil
3206  		default:
3207  			return llvm.Value{}, b.makeError(pos, "binop on signature: "+op.String())
3208  		}
3209  	case *types.Interface:
3210  		switch op {
3211  		case token.EQL, token.NEQ: // ==, !=
3212  			nilInterface := llvm.ConstNull(x.Type())
3213  			var result llvm.Value
3214  			if x == nilInterface || y == nilInterface {
3215  				// An interface value is compared against nil.
3216  				// This is a very common case and is easy to optimize: simply
3217  				// compare the typecodes (of which one is nil).
3218  				typecodeX := b.CreateExtractValue(x, 0, "")
3219  				typecodeY := b.CreateExtractValue(y, 0, "")
3220  				result = b.CreateICmp(llvm.IntEQ, typecodeX, typecodeY, "")
3221  			} else {
3222  				// Fall back to a full interface comparison.
3223  				result = b.createRuntimeCall("interfaceEqual", []llvm.Value{x, y}, "")
3224  			}
3225  			if op == token.NEQ {
3226  				result = b.CreateNot(result, "")
3227  			}
3228  			return result, nil
3229  		default:
3230  			return llvm.Value{}, b.makeError(pos, "binop on interface: "+op.String())
3231  		}
3232  	case *types.Chan, *types.Map, *types.Pointer:
3233  		// Maps are in general not comparable, but can be compared against nil
3234  		// (which is a nil pointer). This means they can be trivially compared
3235  		// by treating them as a pointer.
3236  		// Channels behave as pointers in that they are equal as long as they
3237  		// are created with the same call to make or if both are nil.
3238  		switch op {
3239  		case token.EQL: // ==
3240  			return b.CreateICmp(llvm.IntEQ, x, y, ""), nil
3241  		case token.NEQ: // !=
3242  			return b.CreateICmp(llvm.IntNE, x, y, ""), nil
3243  		default:
3244  			return llvm.Value{}, b.makeError(pos, "todo: binop on pointer: "+op.String())
3245  		}
3246  	case *types.Slice:
3247  		// Moxie: []byte is the text type (replaces string) and supports
3248  		// full comparison (==, !=, <, <=, >, >=) via runtime string functions.
3249  		// Other slice types only support nil comparison.
3250  		if basic, ok := typ.Elem().(*types.Basic); ok && basic.Kind() == types.Byte {
3251  			switch op {
3252  			case token.OR: // Moxie: | is the only text concat operator
3253  				return b.createRuntimeCall("stringConcat", []llvm.Value{x, y}, ""), nil
3254  			case token.EQL:
3255  				return b.createRuntimeCall("stringEqual", []llvm.Value{x, y}, ""), nil
3256  			case token.NEQ:
3257  				result := b.createRuntimeCall("stringEqual", []llvm.Value{x, y}, "")
3258  				return b.CreateNot(result, ""), nil
3259  			case token.LSS:
3260  				return b.createRuntimeCall("stringLess", []llvm.Value{x, y}, ""), nil
3261  			case token.LEQ:
3262  				result := b.createRuntimeCall("stringLess", []llvm.Value{y, x}, "")
3263  				return b.CreateNot(result, ""), nil
3264  			case token.GTR:
3265  				return b.createRuntimeCall("stringLess", []llvm.Value{y, x}, ""), nil
3266  			case token.GEQ:
3267  				result := b.createRuntimeCall("stringLess", []llvm.Value{x, y}, "")
3268  				return b.CreateNot(result, ""), nil
3269  			default:
3270  				return llvm.Value{}, b.makeError(pos, "todo: binop on []byte: "+op.String())
3271  			}
3272  		}
3273  		// Moxie: | on any slice type is concatenation.
3274  		// Uses sliceAppend with cap=len to force fresh allocation.
3275  		if op == token.OR {
3276  			xPtr := b.CreateExtractValue(x, 0, "concat.xPtr")
3277  			xLen := b.CreateExtractValue(x, 1, "concat.xLen")
3278  			yPtr := b.CreateExtractValue(y, 0, "concat.yPtr")
3279  			yLen := b.CreateExtractValue(y, 1, "concat.yLen")
3280  			elemType := b.getLLVMType(typ.Elem())
3281  			elemSize := llvm.ConstInt(b.uintptrType, b.targetData.TypeAllocSize(elemType), false)
3282  			result := b.createRuntimeCall("sliceAppend", []llvm.Value{xPtr, yPtr, xLen, xLen, yLen, elemSize}, "concat.new")
3283  			newPtr := b.CreateExtractValue(result, 0, "concat.ptr")
3284  			newLen := b.CreateExtractValue(result, 1, "concat.len")
3285  			newCap := b.CreateExtractValue(result, 2, "concat.cap")
3286  			newSlice := llvm.Undef(x.Type())
3287  			newSlice = b.CreateInsertValue(newSlice, newPtr, 0, "")
3288  			newSlice = b.CreateInsertValue(newSlice, newLen, 1, "")
3289  			newSlice = b.CreateInsertValue(newSlice, newCap, 2, "")
3290  			return newSlice, nil
3291  		}
3292  		// Moxie: slices of comparable types support == and !=.
3293  		// Compare lengths first, then data byte-by-byte via runtime.
3294  		if op != token.EQL && op != token.NEQ {
3295  			return llvm.Value{}, b.makeError(pos, "todo: binop on slice: "+op.String())
3296  		}
3297  		xPtr := b.CreateExtractValue(x, 0, "")
3298  		xLen := b.CreateExtractValue(x, 1, "")
3299  		yPtr := b.CreateExtractValue(y, 0, "")
3300  		yLen := b.CreateExtractValue(y, 1, "")
3301  		elemSize := b.targetData.TypeAllocSize(b.getLLVMType(typ.Elem()))
3302  		elemSizeVal := llvm.ConstInt(b.uintptrType, elemSize, false)
3303  		result := b.createRuntimeCall("sliceEqual", []llvm.Value{xPtr, yPtr, xLen, yLen, elemSizeVal}, "")
3304  		switch op {
3305  		case token.EQL:
3306  			return result, nil
3307  		case token.NEQ:
3308  			return b.CreateNot(result, ""), nil
3309  		default:
3310  			panic("unreachable")
3311  		}
3312  	case *types.Array:
3313  		// Compare each array element and combine the result. From the spec:
3314  		//     Array values are comparable if values of the array element type
3315  		//     are comparable. Two array values are equal if their corresponding
3316  		//     elements are equal.
3317  		result := llvm.ConstInt(b.ctx.Int1Type(), 1, true)
3318  		for i := 0; i < int(typ.Len()); i++ {
3319  			xField := b.CreateExtractValue(x, i, "")
3320  			yField := b.CreateExtractValue(y, i, "")
3321  			fieldEqual, err := b.createBinOp(token.EQL, typ.Elem(), typ.Elem(), xField, yField, pos)
3322  			if err != nil {
3323  				return llvm.Value{}, err
3324  			}
3325  			result = b.CreateAnd(result, fieldEqual, "")
3326  		}
3327  		switch op {
3328  		case token.EQL: // ==
3329  			return result, nil
3330  		case token.NEQ: // !=
3331  			return b.CreateNot(result, ""), nil
3332  		default:
3333  			return llvm.Value{}, b.makeError(pos, "unknown: binop on struct: "+op.String())
3334  		}
3335  	case *types.Struct:
3336  		// Compare each struct field and combine the result. From the spec:
3337  		//     Struct values are comparable if all their fields are comparable.
3338  		//     Two struct values are equal if their corresponding non-blank
3339  		//     fields are equal.
3340  		result := llvm.ConstInt(b.ctx.Int1Type(), 1, true)
3341  		for i := 0; i < typ.NumFields(); i++ {
3342  			if typ.Field(i).Name() == "_" {
3343  				// skip blank fields
3344  				continue
3345  			}
3346  			fieldType := typ.Field(i).Type()
3347  			xField := b.CreateExtractValue(x, i, "")
3348  			yField := b.CreateExtractValue(y, i, "")
3349  			fieldEqual, err := b.createBinOp(token.EQL, fieldType, fieldType, xField, yField, pos)
3350  			if err != nil {
3351  				return llvm.Value{}, err
3352  			}
3353  			result = b.CreateAnd(result, fieldEqual, "")
3354  		}
3355  		switch op {
3356  		case token.EQL: // ==
3357  			return result, nil
3358  		case token.NEQ: // !=
3359  			return b.CreateNot(result, ""), nil
3360  		default:
3361  			return llvm.Value{}, b.makeError(pos, "unknown: binop on struct: "+op.String())
3362  		}
3363  	default:
3364  		return llvm.Value{}, b.makeError(pos, "todo: binop type: "+typ.String())
3365  	}
3366  }
3367  
3368  // createConst creates a LLVM constant value from a Go constant.
3369  func (c *compilerContext) createConst(expr *ssa.Const, pos token.Pos) llvm.Value {
3370  	switch typ := expr.Type().Underlying().(type) {
3371  	case *types.Basic:
3372  		if typ.Kind() == types.UntypedNil {
3373  			return llvm.ConstNull(c.dataPtrType)
3374  		}
3375  		llvmType := c.getLLVMType(typ)
3376  		if typ.Info()&types.IsBoolean != 0 {
3377  			n := uint64(0)
3378  			if constant.BoolVal(expr.Value) {
3379  				n = 1
3380  			}
3381  			return llvm.ConstInt(llvmType, n, false)
3382  		} else if typ.Info()&types.IsString != 0 {
3383  			str := constant.StringVal(expr.Value)
3384  			strLen := llvm.ConstInt(c.uintptrType, uint64(len(str)), false)
3385  			var strPtr llvm.Value
3386  			if str != "" {
3387  				objname := c.pkg.Path() + "$string"
3388  				globalType := llvm.ArrayType(c.ctx.Int8Type(), len(str))
3389  				global := llvm.AddGlobal(c.mod, globalType, objname)
3390  				global.SetInitializer(c.ctx.ConstString(str, false))
3391  				global.SetLinkage(llvm.InternalLinkage)
3392  				global.SetGlobalConstant(true)
3393  				global.SetUnnamedAddr(true)
3394  				global.SetAlignment(1)
3395  				if c.Debug {
3396  					// Unfortunately, expr.Pos() is always token.NoPos.
3397  					position := c.program.Fset.Position(pos)
3398  					diglobal := c.dibuilder.CreateGlobalVariableExpression(llvm.Metadata{}, llvm.DIGlobalVariableExpression{
3399  						File:        c.getDIFile(position.Filename),
3400  						Line:        position.Line,
3401  						Type:        c.getDIType(types.NewArray(types.Typ[types.Byte], int64(len(str)))),
3402  						LocalToUnit: true,
3403  						Expr:        c.dibuilder.CreateExpression(nil),
3404  					})
3405  					global.AddMetadata(0, diglobal)
3406  				}
3407  				zero := llvm.ConstInt(c.ctx.Int32Type(), 0, false)
3408  				strPtr = llvm.ConstInBoundsGEP(globalType, global, []llvm.Value{zero, zero})
3409  			} else {
3410  				strPtr = llvm.ConstNull(c.dataPtrType)
3411  			}
3412  			strObj := llvm.ConstNamedStruct(c.getLLVMRuntimeType("_string"), []llvm.Value{strPtr, strLen, strLen})
3413  			return strObj
3414  		} else if typ.Kind() == types.UnsafePointer {
3415  			if !expr.IsNil() {
3416  				value, _ := constant.Uint64Val(constant.ToInt(expr.Value))
3417  				return llvm.ConstIntToPtr(llvm.ConstInt(c.uintptrType, value, false), c.dataPtrType)
3418  			}
3419  			return llvm.ConstNull(c.dataPtrType)
3420  		} else if typ.Info()&types.IsUnsigned != 0 {
3421  			n, _ := constant.Uint64Val(constant.ToInt(expr.Value))
3422  			return llvm.ConstInt(llvmType, n, false)
3423  		} else if typ.Info()&types.IsInteger != 0 { // signed
3424  			n, _ := constant.Int64Val(constant.ToInt(expr.Value))
3425  			return llvm.ConstInt(llvmType, uint64(n), true)
3426  		} else if typ.Info()&types.IsFloat != 0 {
3427  			n, _ := constant.Float64Val(expr.Value)
3428  			return llvm.ConstFloat(llvmType, n)
3429  		} else if typ.Kind() == types.Complex64 {
3430  			r := c.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float32]), pos)
3431  			i := c.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float32]), pos)
3432  			cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.FloatType(), c.ctx.FloatType()}, false))
3433  			cplx = c.builder.CreateInsertValue(cplx, r, 0, "")
3434  			cplx = c.builder.CreateInsertValue(cplx, i, 1, "")
3435  			return cplx
3436  		} else if typ.Kind() == types.Complex128 {
3437  			r := c.createConst(ssa.NewConst(constant.Real(expr.Value), types.Typ[types.Float64]), pos)
3438  			i := c.createConst(ssa.NewConst(constant.Imag(expr.Value), types.Typ[types.Float64]), pos)
3439  			cplx := llvm.Undef(c.ctx.StructType([]llvm.Type{c.ctx.DoubleType(), c.ctx.DoubleType()}, false))
3440  			cplx = c.builder.CreateInsertValue(cplx, r, 0, "")
3441  			cplx = c.builder.CreateInsertValue(cplx, i, 1, "")
3442  			return cplx
3443  		} else {
3444  			panic("unknown constant of basic type: " + expr.String())
3445  		}
3446  	case *types.Chan:
3447  		if expr.Value != nil {
3448  			panic("expected nil chan constant")
3449  		}
3450  		return llvm.ConstNull(c.getLLVMType(expr.Type()))
3451  	case *types.Signature:
3452  		if expr.Value != nil {
3453  			panic("expected nil signature constant")
3454  		}
3455  		return llvm.ConstNull(c.getLLVMType(expr.Type()))
3456  	case *types.Interface:
3457  		if expr.Value != nil {
3458  			panic("expected nil interface constant")
3459  		}
3460  		// Create a generic nil interface with no dynamic type (typecode=0).
3461  		fields := []llvm.Value{
3462  			llvm.ConstInt(c.uintptrType, 0, false),
3463  			llvm.ConstPointerNull(c.dataPtrType),
3464  		}
3465  		return llvm.ConstNamedStruct(c.getLLVMRuntimeType("_interface"), fields)
3466  	case *types.Pointer:
3467  		if expr.Value != nil {
3468  			panic("expected nil pointer constant")
3469  		}
3470  		return llvm.ConstPointerNull(c.getLLVMType(typ))
3471  	case *types.Array:
3472  		if expr.Value != nil {
3473  			panic("expected nil array constant")
3474  		}
3475  		return llvm.ConstNull(c.getLLVMType(expr.Type()))
3476  	case *types.Slice:
3477  		if expr.Value != nil {
3478  			panic("expected nil slice constant")
3479  		}
3480  		// Moxie: use ConstNull with correct type (named for []byte).
3481  		return llvm.ConstNull(c.getLLVMType(expr.Type()))
3482  	case *types.Struct:
3483  		if expr.Value != nil {
3484  			panic("expected nil struct constant")
3485  		}
3486  		return llvm.ConstNull(c.getLLVMType(expr.Type()))
3487  	case *types.Map:
3488  		if !expr.IsNil() {
3489  			// I believe this is not allowed by the Go spec.
3490  			panic("non-nil map constant")
3491  		}
3492  		llvmType := c.getLLVMType(typ)
3493  		return llvm.ConstNull(llvmType)
3494  	default:
3495  		panic("unknown constant: " + expr.String())
3496  	}
3497  }
3498  
3499  // createConvert creates a Go type conversion instruction.
3500  func (b *builder) createConvert(typeFrom, typeTo types.Type, value llvm.Value, pos token.Pos) (llvm.Value, error) {
3501  	llvmTypeFrom := value.Type()
3502  	llvmTypeTo := b.getLLVMType(typeTo)
3503  
3504  	// Conversion between unsafe.Pointer and uintptr.
3505  	isPtrFrom := isPointer(typeFrom.Underlying())
3506  	isPtrTo := isPointer(typeTo.Underlying())
3507  	if isPtrFrom && !isPtrTo {
3508  		return b.CreatePtrToInt(value, llvmTypeTo, ""), nil
3509  	} else if !isPtrFrom && isPtrTo {
3510  		return b.CreateIntToPtr(value, llvmTypeTo, ""), nil
3511  	}
3512  
3513  	// Conversion between pointers and unsafe.Pointer.
3514  	if isPtrFrom && isPtrTo {
3515  		return value, nil
3516  	}
3517  
3518  	switch typeTo := typeTo.Underlying().(type) {
3519  	case *types.Basic:
3520  		sizeFrom := b.targetData.TypeAllocSize(llvmTypeFrom)
3521  
3522  		if typeTo.Info()&types.IsString != 0 {
3523  			switch typeFrom := typeFrom.Underlying().(type) {
3524  			case *types.Basic:
3525  				// Assume a Unicode code point, as that is the only possible
3526  				// value here.
3527  				// Cast to an i32 value as expected by
3528  				// runtime.stringFromUnicode.
3529  				if sizeFrom > 4 {
3530  					value = b.CreateTrunc(value, b.ctx.Int32Type(), "")
3531  				} else if sizeFrom < 4 && typeTo.Info()&types.IsUnsigned != 0 {
3532  					value = b.CreateZExt(value, b.ctx.Int32Type(), "")
3533  				} else if sizeFrom < 4 {
3534  					value = b.CreateSExt(value, b.ctx.Int32Type(), "")
3535  				}
3536  				return b.createRuntimeCall("stringFromUnicode", []llvm.Value{value}, ""), nil
3537  			case *types.Slice:
3538  				switch typeFrom.Elem().(*types.Basic).Kind() {
3539  				case types.Byte:
3540  					// Moxie: string and []byte have identical 3-word layout.
3541  					// No allocation or copy — just repack fields for LLVM type compatibility.
3542  					ptr := b.CreateExtractValue(value, 0, "")
3543  					ln := b.CreateExtractValue(value, 1, "")
3544  					cp := b.CreateExtractValue(value, 2, "")
3545  					str := llvm.Undef(b.getLLVMRuntimeType("_string"))
3546  					str = b.CreateInsertValue(str, ptr, 0, "")
3547  					str = b.CreateInsertValue(str, ln, 1, "")
3548  					str = b.CreateInsertValue(str, cp, 2, "")
3549  					return str, nil
3550  				case types.Rune:
3551  					return b.createRuntimeCall("stringFromRunes", []llvm.Value{value}, ""), nil
3552  				default:
3553  					return llvm.Value{}, b.makeError(pos, "todo: convert to string: "+typeFrom.String())
3554  				}
3555  			default:
3556  				return llvm.Value{}, b.makeError(pos, "todo: convert to string: "+typeFrom.String())
3557  			}
3558  		}
3559  
3560  		typeFrom := typeFrom.Underlying().(*types.Basic)
3561  		sizeTo := b.targetData.TypeAllocSize(llvmTypeTo)
3562  
3563  		if typeFrom.Info()&types.IsInteger != 0 && typeTo.Info()&types.IsInteger != 0 {
3564  			// Conversion between two integers.
3565  			if sizeFrom > sizeTo {
3566  				return b.CreateTrunc(value, llvmTypeTo, ""), nil
3567  			} else if typeFrom.Info()&types.IsUnsigned != 0 { // if unsigned
3568  				return b.CreateZExt(value, llvmTypeTo, ""), nil
3569  			} else { // if signed
3570  				return b.CreateSExt(value, llvmTypeTo, ""), nil
3571  			}
3572  		}
3573  
3574  		if typeFrom.Info()&types.IsFloat != 0 && typeTo.Info()&types.IsFloat != 0 {
3575  			// Conversion between two floats.
3576  			if sizeFrom > sizeTo {
3577  				return b.CreateFPTrunc(value, llvmTypeTo, ""), nil
3578  			} else if sizeFrom < sizeTo {
3579  				return b.CreateFPExt(value, llvmTypeTo, ""), nil
3580  			} else {
3581  				return value, nil
3582  			}
3583  		}
3584  
3585  		if typeFrom.Info()&types.IsFloat != 0 && typeTo.Info()&types.IsInteger != 0 {
3586  			// Conversion from float to int.
3587  			// Passing an out-of-bounds float to LLVM would cause UB, so that UB is trapped by select instructions.
3588  			// The Go specification says that this should be implementation-defined behavior.
3589  			// This implements saturating behavior, except that NaN is mapped to the minimum value.
3590  			var significandBits int
3591  			switch typeFrom.Kind() {
3592  			case types.Float32:
3593  				significandBits = 23
3594  			case types.Float64:
3595  				significandBits = 52
3596  			}
3597  			if typeTo.Info()&types.IsUnsigned != 0 { // if unsigned
3598  				// Select the maximum value for this unsigned integer type.
3599  				max := ^(^uint64(0) << uint(llvmTypeTo.IntTypeWidth()))
3600  				maxFloat := float64(max)
3601  				if bits.Len64(max) > significandBits {
3602  					// Round the max down to fit within the significand.
3603  					maxFloat = float64(max & (^uint64(0) << uint(bits.Len64(max)-significandBits)))
3604  				}
3605  
3606  				// Check if the value is in-bounds (0 <= value <= max).
3607  				positive := b.CreateFCmp(llvm.FloatOLE, llvm.ConstNull(llvmTypeFrom), value, "positive")
3608  				withinMax := b.CreateFCmp(llvm.FloatOLE, value, llvm.ConstFloat(llvmTypeFrom, maxFloat), "withinmax")
3609  				inBounds := b.CreateAnd(positive, withinMax, "inbounds")
3610  
3611  				// Assuming that the value is out-of-bounds, select a saturated value.
3612  				saturated := b.CreateSelect(positive,
3613  					llvm.ConstInt(llvmTypeTo, max, false), // value > max
3614  					llvm.ConstNull(llvmTypeTo),            // value < 0 (or NaN)
3615  					"saturated",
3616  				)
3617  
3618  				// Do a normal conversion.
3619  				normal := b.CreateFPToUI(value, llvmTypeTo, "normal")
3620  
3621  				return b.CreateSelect(inBounds, normal, saturated, ""), nil
3622  			} else { // if signed
3623  				// Select the minimum value for this signed integer type.
3624  				min := uint64(1) << uint(llvmTypeTo.IntTypeWidth()-1)
3625  				minFloat := -float64(min)
3626  
3627  				// Select the maximum value for this signed integer type.
3628  				max := ^(^uint64(0) << uint(llvmTypeTo.IntTypeWidth()-1))
3629  				maxFloat := float64(max)
3630  				if bits.Len64(max) > significandBits {
3631  					// Round the max down to fit within the significand.
3632  					maxFloat = float64(max & (^uint64(0) << uint(bits.Len64(max)-significandBits)))
3633  				}
3634  
3635  				// Check if the value is in-bounds (min <= value <= max).
3636  				aboveMin := b.CreateFCmp(llvm.FloatOLE, llvm.ConstFloat(llvmTypeFrom, minFloat), value, "abovemin")
3637  				belowMax := b.CreateFCmp(llvm.FloatOLE, value, llvm.ConstFloat(llvmTypeFrom, maxFloat), "belowmax")
3638  				inBounds := b.CreateAnd(aboveMin, belowMax, "inbounds")
3639  
3640  				// Assuming that the value is out-of-bounds, select a saturated value.
3641  				saturated := b.CreateSelect(aboveMin,
3642  					llvm.ConstInt(llvmTypeTo, max, false), // value > max
3643  					llvm.ConstInt(llvmTypeTo, min, false), // value < min
3644  					"saturated",
3645  				)
3646  
3647  				// Map NaN to 0.
3648  				saturated = b.CreateSelect(b.CreateFCmp(llvm.FloatUNO, value, value, "isnan"),
3649  					llvm.ConstNull(llvmTypeTo),
3650  					saturated,
3651  					"remapped",
3652  				)
3653  
3654  				// Do a normal conversion.
3655  				normal := b.CreateFPToSI(value, llvmTypeTo, "normal")
3656  
3657  				return b.CreateSelect(inBounds, normal, saturated, ""), nil
3658  			}
3659  		}
3660  
3661  		if typeFrom.Info()&types.IsInteger != 0 && typeTo.Info()&types.IsFloat != 0 {
3662  			// Conversion from int to float.
3663  			if typeFrom.Info()&types.IsUnsigned != 0 { // if unsigned
3664  				return b.CreateUIToFP(value, llvmTypeTo, ""), nil
3665  			} else { // if signed
3666  				return b.CreateSIToFP(value, llvmTypeTo, ""), nil
3667  			}
3668  		}
3669  
3670  		if typeFrom.Kind() == types.Complex128 && typeTo.Kind() == types.Complex64 {
3671  			// Conversion from complex128 to complex64.
3672  			r := b.CreateExtractValue(value, 0, "real.f64")
3673  			i := b.CreateExtractValue(value, 1, "imag.f64")
3674  			r = b.CreateFPTrunc(r, b.ctx.FloatType(), "real.f32")
3675  			i = b.CreateFPTrunc(i, b.ctx.FloatType(), "imag.f32")
3676  			cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.FloatType(), b.ctx.FloatType()}, false))
3677  			cplx = b.CreateInsertValue(cplx, r, 0, "")
3678  			cplx = b.CreateInsertValue(cplx, i, 1, "")
3679  			return cplx, nil
3680  		}
3681  
3682  		if typeFrom.Kind() == types.Complex64 && typeTo.Kind() == types.Complex128 {
3683  			// Conversion from complex64 to complex128.
3684  			r := b.CreateExtractValue(value, 0, "real.f32")
3685  			i := b.CreateExtractValue(value, 1, "imag.f32")
3686  			r = b.CreateFPExt(r, b.ctx.DoubleType(), "real.f64")
3687  			i = b.CreateFPExt(i, b.ctx.DoubleType(), "imag.f64")
3688  			cplx := llvm.Undef(b.ctx.StructType([]llvm.Type{b.ctx.DoubleType(), b.ctx.DoubleType()}, false))
3689  			cplx = b.CreateInsertValue(cplx, r, 0, "")
3690  			cplx = b.CreateInsertValue(cplx, i, 1, "")
3691  			return cplx, nil
3692  		}
3693  
3694  		return llvm.Value{}, b.makeError(pos, "todo: convert: basic non-integer type: "+typeFrom.String()+" -> "+typeTo.String())
3695  
3696  	case *types.Slice:
3697  		// Moxie: allow conversion from string or []byte (string=[]byte).
3698  		if !isStringLike(typeFrom.Underlying()) {
3699  			return llvm.Value{}, b.makeError(pos, fmt.Sprintf("cannot convert %s to %s (not string-like)", typeFrom, typeTo))
3700  		}
3701  
3702  		elemType := typeTo.Elem().Underlying().(*types.Basic) // must be byte or rune
3703  		switch elemType.Kind() {
3704  		case types.Byte:
3705  			// Moxie: string and []byte have identical 3-word layout.
3706  			// No allocation or copy — just repack fields for LLVM type compatibility.
3707  			ptr := b.CreateExtractValue(value, 0, "")
3708  			ln := b.CreateExtractValue(value, 1, "")
3709  			cp := b.CreateExtractValue(value, 2, "")
3710  			slice := llvm.Undef(b.getLLVMType(typeTo))
3711  			slice = b.CreateInsertValue(slice, ptr, 0, "")
3712  			slice = b.CreateInsertValue(slice, ln, 1, "")
3713  			slice = b.CreateInsertValue(slice, cp, 2, "")
3714  			return slice, nil
3715  		case types.Rune:
3716  			return b.createRuntimeCall("stringToRunes", []llvm.Value{value}, ""), nil
3717  		default:
3718  			panic("unexpected type in string to slice conversion")
3719  		}
3720  
3721  	default:
3722  		return llvm.Value{}, b.makeError(pos, "todo: convert "+typeTo.String()+" <- "+typeFrom.String())
3723  	}
3724  }
3725  
3726  // createUnOp creates LLVM IR for a given Go unary operation.
3727  // Most unary operators are pretty simple, such as the not and minus operator
3728  // which can all be directly lowered to IR. However, there is also the channel
3729  // receive operator which is handled in the runtime directly.
3730  func (b *builder) createUnOp(unop *ssa.UnOp) (llvm.Value, error) {
3731  	x := b.getValue(unop.X, getPos(unop))
3732  	switch unop.Op {
3733  	case token.NOT: // !x
3734  		return b.CreateNot(x, ""), nil
3735  	case token.SUB: // -x
3736  		if typ, ok := unop.X.Type().Underlying().(*types.Basic); ok {
3737  			if typ.Info()&types.IsInteger != 0 {
3738  				return b.CreateSub(llvm.ConstInt(x.Type(), 0, false), x, ""), nil
3739  			} else if typ.Info()&types.IsFloat != 0 {
3740  				return b.CreateFNeg(x, ""), nil
3741  			} else if typ.Info()&types.IsComplex != 0 {
3742  				// Negate both components of the complex number.
3743  				r := b.CreateExtractValue(x, 0, "r")
3744  				i := b.CreateExtractValue(x, 1, "i")
3745  				r = b.CreateFNeg(r, "")
3746  				i = b.CreateFNeg(i, "")
3747  				cplx := llvm.Undef(x.Type())
3748  				cplx = b.CreateInsertValue(cplx, r, 0, "")
3749  				cplx = b.CreateInsertValue(cplx, i, 1, "")
3750  				return cplx, nil
3751  			} else {
3752  				return llvm.Value{}, b.makeError(unop.Pos(), "todo: unknown basic type for negate: "+typ.String())
3753  			}
3754  		} else {
3755  			return llvm.Value{}, b.makeError(unop.Pos(), "todo: unknown type for negate: "+unop.X.Type().Underlying().String())
3756  		}
3757  	case token.MUL: // *x, dereference pointer
3758  		valueType := b.getLLVMType(unop.X.Type().Underlying().(*types.Pointer).Elem())
3759  		if b.targetData.TypeAllocSize(valueType) == 0 {
3760  			return llvm.ConstNull(valueType), nil
3761  		} else {
3762  			b.createNilCheck(unop.X, x, "deref")
3763  			load := b.CreateLoad(valueType, x, "")
3764  			return load, nil
3765  		}
3766  	case token.XOR: // ^x, toggle all bits in integer
3767  		return b.CreateXor(x, llvm.ConstInt(x.Type(), ^uint64(0), false), ""), nil
3768  	case token.ARROW: // <-x, receive from channel
3769  		return b.createChanRecv(unop), nil
3770  	default:
3771  		return llvm.Value{}, b.makeError(unop.Pos(), "todo: unknown unop")
3772  	}
3773  }
3774  
3775  // typeContainsPointers returns true if the Go type contains any pointers,
3776  // slices, maps, channels, interfaces, strings, or function values that
3777  // would need deep copying when crossing an arena boundary.
3778  func typeContainsPointers(t types.Type) bool {
3779  	switch typ := t.Underlying().(type) {
3780  	case *types.Basic:
3781  		return typ.Info()&types.IsString != 0 // string = []byte, has pointer
3782  	case *types.Pointer, *types.Map, *types.Chan, *types.Interface,
3783  		*types.Signature, *types.Slice:
3784  		return true
3785  	case *types.Array:
3786  		return typeContainsPointers(typ.Elem())
3787  	case *types.Struct:
3788  		for i := 0; i < typ.NumFields(); i++ {
3789  			if typeContainsPointers(typ.Field(i).Type()) {
3790  				return true
3791  			}
3792  		}
3793  		return false
3794  	}
3795  	return false
3796  }
3797  
3798  var dcCount int
3799  
3800  // emitReturnCopy handles the full return-copy sequence for a single value:
3801  // 1. If not local, just restore and return as-is
3802  // 2. If local: save to stack alloca, ArenaRestore, alloc in caller region,
3803  //    memcpy from stack to caller region, return caller-region pointer.
3804  func (b *builder) emitReturnCopy(val llvm.Value, ssaVal ssa.Value) llvm.Value {
3805  	goType := ssaVal.Type()
3806  	if !typeContainsPointers(goType) || !b.valueIsLocal(ssaVal) {
3807  		// No copy needed. Caller handles arena switch + free.
3808  		return val
3809  	}
3810  	return b.emitReturnCopyValue(val, goType)
3811  }
3812  
3813  // emitReturnCopyValue deep-copies a return value from fn_arena to outer_arena.
3814  // No stack intermediary needed - source (fn_arena) and destination (outer_arena)
3815  // are in different mmap regions, no overlap.
3816  func (b *builder) emitReturnCopyValue(val llvm.Value, goType types.Type) llvm.Value {
3817  	// Caller already switched to outer arena. Deep copy reads from fn_arena
3818  	// (still alive), allocates in outer arena.
3819  	return b.emitDeepCopy(val, goType)
3820  }
3821  
3822  // emitReturnDeepCopy emits a deep copy of val if its type contains pointers
3823  // AND the value was allocated by this function. Parameters and globals are
3824  // borrows - they pass through without copying.
3825  func (b *builder) emitReturnDeepCopy(val llvm.Value, ssaVal ssa.Value) llvm.Value {
3826  	goType := ssaVal.Type()
3827  	if !typeContainsPointers(goType) {
3828  		return val
3829  	}
3830  	if !b.valueIsLocal(ssaVal) {
3831  		return val
3832  	}
3833  	dcCount++
3834  	if dcCount%100 == 0 {
3835  		fmt.Fprintf(os.Stderr, "[dc] %d copies emitted, fn=%s type=%s\n", dcCount, b.fn.Name(), goType)
3836  	}
3837  	return b.emitDeepCopy(val, goType)
3838  }
3839  
3840  // valueIsLocal returns true if the SSA value was allocated by this function
3841  // (not a parameter, global, or free variable). Only locally allocated values
3842  // need deep copying at return - everything else is a borrow from the caller.
3843  func (b *builder) valueIsLocal(v ssa.Value) bool {
3844  	return valueIsLocalRec(v, make(map[ssa.Value]bool))
3845  }
3846  
3847  func valueIsLocalRec(v ssa.Value, seen map[ssa.Value]bool) bool {
3848  	if seen[v] {
3849  		return false
3850  	}
3851  	seen[v] = true
3852  	switch v.(type) {
3853  	case *ssa.Parameter, *ssa.FreeVar, *ssa.Global, *ssa.Const, *ssa.Builtin:
3854  		return false // borrows from caller or constants
3855  	case *ssa.Alloc, *ssa.MakeSlice, *ssa.MakeMap, *ssa.MakeChan, *ssa.MakeClosure:
3856  		return true // this function allocated it
3857  	case *ssa.Call:
3858  		// Runtime functions are exempt from arena discipline - their
3859  		// returns are NOT deep-copied by the callee. Treat as local.
3860  		if fn := v.(*ssa.Call).Common().StaticCallee(); fn != nil {
3861  			if fn.Pkg != nil && fn.Pkg.Pkg.Path() == "runtime" {
3862  				return true
3863  			}
3864  		}
3865  		return false // non-runtime callee already deep-copied its return
3866  	}
3867  	// For derived values (FieldAddr, IndexAddr, Slice, Phi, etc.),
3868  	// trace through operands.
3869  	switch vv := v.(type) {
3870  	case *ssa.FieldAddr:
3871  		return valueIsLocalRec(vv.X, seen)
3872  	case *ssa.IndexAddr:
3873  		return valueIsLocalRec(vv.X, seen)
3874  	case *ssa.Slice:
3875  		return valueIsLocalRec(vv.X, seen)
3876  	case *ssa.UnOp:
3877  		return valueIsLocalRec(vv.X, seen)
3878  	case *ssa.MakeInterface:
3879  		return valueIsLocalRec(vv.X, seen)
3880  	case *ssa.ChangeInterface:
3881  		return valueIsLocalRec(vv.X, seen)
3882  	case *ssa.ChangeType:
3883  		return valueIsLocalRec(vv.X, seen)
3884  	case *ssa.Convert:
3885  		return valueIsLocalRec(vv.X, seen)
3886  	case *ssa.TypeAssert:
3887  		return valueIsLocalRec(vv.X, seen)
3888  	case *ssa.Extract:
3889  		return valueIsLocalRec(vv.Tuple, seen)
3890  	case *ssa.Phi:
3891  		// Phi: local if ANY edge is local (conservative - might copy unnecessarily)
3892  		for _, edge := range vv.Edges {
3893  			if valueIsLocalRec(edge, seen) {
3894  				return true
3895  			}
3896  		}
3897  		return false
3898  	case *ssa.BinOp:
3899  		// String/slice concat via | produces a new allocation
3900  		if b, ok := vv.Type().Underlying().(*types.Basic); ok && b.Info()&types.IsString != 0 {
3901  			return true
3902  		}
3903  		if _, ok := vv.Type().Underlying().(*types.Slice); ok {
3904  			return true // slice concat
3905  		}
3906  		return false
3907  	}
3908  	return true // conservative: assume local if unknown
3909  }
3910  
3911  // emitCopySliceField copies a slice/string field inside a generated copy
3912  // function using alloc+memcpy directly (no runtime helper call, avoids
3913  // LLVM named-vs-anonymous struct type mismatch).
3914  func (c *compilerContext) emitCopySliceField(oldVal llvm.Value, fieldLLVM llvm.Type) llvm.Value {
3915  	// Extract ptr and len from the slice struct
3916  	slicePtr := c.builder.CreateExtractValue(oldVal, 0, "dc.sp")
3917  	sliceLen := c.builder.CreateExtractValue(oldVal, 1, "dc.sl")
3918  
3919  	// Null check
3920  	isNull := c.builder.CreateICmp(llvm.IntEQ, slicePtr, llvm.ConstNull(c.dataPtrType), "dc.sn")
3921  	fn := c.builder.GetInsertBlock().Parent()
3922  	copyBB := c.ctx.AddBasicBlock(fn, "dc.scopy")
3923  	mergeBB := c.ctx.AddBasicBlock(fn, "dc.smerge")
3924  	origBB := c.builder.GetInsertBlock()
3925  	c.builder.CreateCondBr(isNull, mergeBB, copyBB)
3926  
3927  	c.builder.SetInsertPointAtEnd(copyBB)
3928  	allocFnType, allocFn := c.getFunction(c.program.ImportedPackage("runtime").Members["alloc"].(*ssa.Function))
3929  	newPtr := c.builder.CreateCall(allocFnType, allocFn, []llvm.Value{sliceLen, llvm.ConstNull(c.dataPtrType), llvm.Undef(c.dataPtrType)}, "dc.sb")
3930  	memcpyFnType, memcpyFn := c.getFunction(c.program.ImportedPackage("runtime").Members["memcpy"].(*ssa.Function))
3931  	c.builder.CreateCall(memcpyFnType, memcpyFn, []llvm.Value{newPtr, slicePtr, sliceLen, llvm.Undef(c.dataPtrType)}, "")
3932  	// Build new slice {newPtr, len, len}
3933  	s1 := c.builder.CreateInsertValue(llvm.Undef(fieldLLVM), newPtr, 0, "")
3934  	s2 := c.builder.CreateInsertValue(s1, sliceLen, 1, "")
3935  	s3 := c.builder.CreateInsertValue(s2, sliceLen, 2, "")
3936  	c.builder.CreateBr(mergeBB)
3937  	copyEnd := c.builder.GetInsertBlock()
3938  
3939  	c.builder.SetInsertPointAtEnd(mergeBB)
3940  	phi := c.builder.CreatePHI(fieldLLVM, "dc.sphi")
3941  	phi.AddIncoming([]llvm.Value{oldVal, s3}, []llvm.BasicBlock{origBB, copyEnd})
3942  	return phi
3943  }
3944  
3945  // emitArenaCopySlice calls runtime.arenaCopySliceIfLocal with the saved
3946  // arena mark. Only copies if the backing data is above the mark.
3947  func (b *builder) emitArenaCopySlice(val llvm.Value) llvm.Value {
3948  	result := b.createRuntimeCall("arenaCopySliceIfLocal", []llvm.Value{val, b.arenaMarkVal}, "dc.s")
3949  	if result.Type() == val.Type() {
3950  		return result
3951  	}
3952  	tmp := llvmutil.CreateEntryBlockAlloca(b.Builder, val.Type(), "dc.cast")
3953  	b.CreateStore(result, tmp)
3954  	return b.CreateLoad(val.Type(), tmp, "dc.cast")
3955  }
3956  
3957  // getOrCreatePtrCopyFn returns a module-level function that deep-copies
3958  // *T. Generated once per type, called at every return site that needs it.
3959  func (c *compilerContext) getOrCreatePtrCopyFn(typ *types.Pointer) llvm.Value {
3960  	name := "arenaCopy." + typ.String()
3961  	if fn := c.mod.NamedFunction(name); !fn.IsNil() {
3962  		return fn
3963  	}
3964  	elemType := c.getLLVMType(typ.Elem())
3965  	fnType := llvm.FunctionType(c.dataPtrType, []llvm.Type{c.dataPtrType}, false)
3966  	fn := llvm.AddFunction(c.mod, name, fnType)
3967  	fn.SetLinkage(llvm.InternalLinkage)
3968  
3969  	entry := c.ctx.AddBasicBlock(fn, "entry")
3970  	copyBB := c.ctx.AddBasicBlock(fn, "copy")
3971  	retNull := c.ctx.AddBasicBlock(fn, "retnull")
3972  
3973  	savedBlock := c.builder.GetInsertBlock()
3974  	c.builder.SetInsertPointAtEnd(entry)
3975  	src := fn.Param(0)
3976  	isNull := c.builder.CreateICmp(llvm.IntEQ, src, llvm.ConstNull(c.dataPtrType), "")
3977  	c.builder.CreateCondBr(isNull, retNull, copyBB)
3978  
3979  	c.builder.SetInsertPointAtEnd(retNull)
3980  	c.builder.CreateRet(llvm.ConstNull(c.dataPtrType))
3981  
3982  	c.builder.SetInsertPointAtEnd(copyBB)
3983  	size := c.targetData.TypeAllocSize(elemType)
3984  	sizeVal := llvm.ConstInt(c.uintptrType, size, false)
3985  
3986  	// Load entire source struct to stack BEFORE alloc.
3987  	// After ArenaRestore, source and destination may overlap at the same
3988  	// arena offset. ArenaAlloc's memzero would wipe source data.
3989  	// Stack locals survive because they're not in the arena.
3990  	srcStack := c.builder.CreateAlloca(elemType, "dc.src")
3991  	srcLoaded := c.builder.CreateLoad(elemType, src, "dc.srcld")
3992  	c.builder.CreateStore(srcLoaded, srcStack)
3993  
3994  	// Alloc destination in current arena region (caller's region after restore).
3995  	layoutVal := llvm.ConstNull(c.dataPtrType)
3996  	allocFnType, allocFn := c.getFunction(c.program.ImportedPackage("runtime").Members["alloc"].(*ssa.Function))
3997  	dst := c.builder.CreateCall(allocFnType, allocFn, []llvm.Value{sizeVal, layoutVal, llvm.Undef(c.dataPtrType)}, "dst")
3998  
3999  	// Memcpy from stack to destination.
4000  	memcpyFnType, memcpyFn := c.getFunction(c.program.ImportedPackage("runtime").Members["memcpy"].(*ssa.Function))
4001  	c.builder.CreateCall(memcpyFnType, memcpyFn, []llvm.Value{dst, srcStack, sizeVal, llvm.Undef(c.dataPtrType)}, "")
4002  
4003  	// Recurse into pointer-containing fields.
4004  	if st, ok := typ.Elem().Underlying().(*types.Struct); ok {
4005  		for i := 0; i < st.NumFields(); i++ {
4006  			ft := st.Field(i).Type()
4007  			if !typeContainsPointers(ft) {
4008  				continue
4009  			}
4010  			fieldLLVM := c.getLLVMType(ft)
4011  			// Read field from STACK copy (safe from overlap).
4012  			srcGep := c.builder.CreateStructGEP(elemType, srcStack, i, "dc.sgep")
4013  			oldVal := c.builder.CreateLoad(fieldLLVM, srcGep, "dc.fld")
4014  			var newVal llvm.Value
4015  			switch ftu := ft.Underlying().(type) {
4016  			case *types.Basic:
4017  				if ftu.Info()&types.IsString != 0 {
4018  					newVal = c.emitCopySliceField(oldVal, fieldLLVM)
4019  				} else {
4020  					continue
4021  				}
4022  			case *types.Map:
4023  				hmCopyType, hmCopyFn := c.getFunction(c.program.ImportedPackage("runtime").Members["hashmapCopy"].(*ssa.Function))
4024  				newVal = c.builder.CreateCall(hmCopyType, hmCopyFn, []llvm.Value{oldVal, llvm.Undef(c.dataPtrType)}, "dc.map")
4025  			case *types.Slice:
4026  				newVal = c.emitCopySliceField(oldVal, fieldLLVM)
4027  			case *types.Pointer:
4028  				subCopyFn := c.getOrCreatePtrCopyFn(ftu)
4029  				subCopyFnType := llvm.FunctionType(c.dataPtrType, []llvm.Type{c.dataPtrType}, false)
4030  				newVal = c.builder.CreateCall(subCopyFnType, subCopyFn, []llvm.Value{oldVal}, "dc.sub")
4031  			default:
4032  				continue
4033  			}
4034  			// Write to destination
4035  			dstGep := c.builder.CreateStructGEP(elemType, dst, i, "dc.dgep")
4036  			c.builder.CreateStore(newVal, dstGep)
4037  		}
4038  	}
4039  
4040  	c.builder.CreateRet(dst)
4041  
4042  	c.builder.SetInsertPointAtEnd(savedBlock)
4043  	return fn
4044  }
4045  
4046  // emitDeepCopy emits a single runtime call to deep-copy a value.
4047  // For slices/strings: runtime.arenaCopySlice.
4048  // For pointers: runtime.arenaCopyPtr with element size.
4049  // For structs with pointer fields: recurse per field.
4050  func (b *builder) emitDeepCopy(val llvm.Value, goType types.Type) llvm.Value {
4051  	switch typ := goType.Underlying().(type) {
4052  	case *types.Basic:
4053  		if typ.Info()&types.IsString != 0 {
4054  			return b.emitArenaCopySlice(val)
4055  		}
4056  		return val
4057  	case *types.Slice:
4058  		return b.emitArenaCopySlice(val)
4059  	case *types.Pointer:
4060  		copyFn := b.getOrCreatePtrCopyFn(typ)
4061  		copyFnType := llvm.FunctionType(b.dataPtrType, []llvm.Type{b.dataPtrType}, false)
4062  		return b.CreateCall(copyFnType, copyFn, []llvm.Value{val}, "dc.p")
4063  	case *types.Map:
4064  		return b.createRuntimeCall("hashmapCopy", []llvm.Value{val}, "dc.m")
4065  	case *types.Struct:
4066  		result := val
4067  		for i := 0; i < typ.NumFields(); i++ {
4068  			ft := typ.Field(i).Type()
4069  			if !typeContainsPointers(ft) {
4070  				continue
4071  			}
4072  			field := b.CreateExtractValue(val, i, "dc.sf")
4073  			copied := b.emitDeepCopy(field, ft)
4074  			result = b.CreateInsertValue(result, copied, i, "dc.sf")
4075  		}
4076  		return result
4077  	case *types.Array:
4078  		if !typeContainsPointers(typ.Elem()) {
4079  			return val
4080  		}
4081  		result := val
4082  		for i := int64(0); i < typ.Len(); i++ {
4083  			elem := b.CreateExtractValue(val, int(i), "dc.ae")
4084  			copied := b.emitDeepCopy(elem, typ.Elem())
4085  			result = b.CreateInsertValue(result, copied, int(i), "dc.ae")
4086  		}
4087  		return result
4088  	}
4089  	return val
4090  }
4091