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