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