Refactor: Use our own types as much as possible from the analysis

Previously, mostly the types from the ssa package were used directly
with the types from analysis.go as an overlay. This commit uses these
types everywhere and renames a few things here and there to make things
clearer.
Этот коммит содержится в:
Ayke van Laethem 2018-08-17 23:12:15 +02:00
родитель a97ca91c1f
коммит 574c7ec047
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: E97FF5335DFDFDED
3 изменённых файлов: 414 добавлений и 418 удалений

Просмотреть файл

@ -7,125 +7,13 @@ import (
"golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa"
) )
// Analysis results over a whole program.
type Analysis struct {
functions map[*ssa.Function]*FuncMeta
needsScheduler bool
goCalls []*ssa.Go
typesWithMethods map[string]*TypeMeta
typesWithoutMethods map[string]int
methodSignatureNames map[string]int
}
// Some analysis results of a single function.
type FuncMeta struct {
f *ssa.Function
blocking bool
parents []*ssa.Function // calculated by AnalyseCallgraph
children []*ssa.Function
}
type TypeMeta struct {
t types.Type
Num int
Methods map[string]*types.Selection
}
// Return a new Analysis object.
func NewAnalysis() *Analysis {
return &Analysis{
functions: make(map[*ssa.Function]*FuncMeta),
typesWithMethods: make(map[string]*TypeMeta),
typesWithoutMethods: make(map[string]int),
methodSignatureNames: make(map[string]int),
}
}
// Add a given package to the analyzer, to be analyzed later.
func (a *Analysis) AddPackage(pkg *ssa.Package) {
for _, member := range pkg.Members {
switch member := member.(type) {
case *ssa.Function:
if isCGoInternal(member.Name()) || getCName(member.Name()) != "" {
continue
}
a.addFunction(member)
case *ssa.Type:
methods := getAllMethods(pkg.Prog, member.Type())
if types.IsInterface(member.Type()) {
for _, method := range methods {
a.MethodName(method.Obj().(*types.Func))
}
} else { // named type
for _, method := range methods {
a.addFunction(pkg.Prog.MethodValue(method))
}
}
}
}
}
// Analyze the given function quickly without any recursion, and add it to the
// list of functions in the analyzer.
func (a *Analysis) addFunction(f *ssa.Function) {
fm := &FuncMeta{}
for _, block := range f.Blocks {
for _, instr := range block.Instrs {
switch instr := instr.(type) {
case *ssa.Call:
if instr.Common().IsInvoke() {
name := a.MethodName(instr.Common().Method)
a.methodSignatureNames[name] = len(a.methodSignatureNames)
} else {
switch call := instr.Call.Value.(type) {
case *ssa.Builtin:
// ignore
case *ssa.Function:
if isCGoInternal(call.Name()) || getCName(call.Name()) != "" {
continue
}
name := getFunctionName(call, false)
if name == "runtime.Sleep" {
fm.blocking = true
}
fm.children = append(fm.children, call)
}
}
case *ssa.MakeInterface:
methods := getAllMethods(f.Prog, instr.X.Type())
if _, ok := a.typesWithMethods[instr.X.Type().String()]; !ok && len(methods) > 0 {
meta := &TypeMeta{
t: instr.X.Type(),
Num: len(a.typesWithMethods),
Methods: make(map[string]*types.Selection),
}
for _, sel := range methods {
name := a.MethodName(sel.Obj().(*types.Func))
meta.Methods[name] = sel
}
a.typesWithMethods[instr.X.Type().String()] = meta
} else if _, ok := a.typesWithoutMethods[instr.X.Type().String()]; !ok && len(methods) == 0 {
a.typesWithoutMethods[instr.X.Type().String()] = len(a.typesWithoutMethods)
}
case *ssa.Go:
a.goCalls = append(a.goCalls, instr)
}
}
}
a.functions[f] = fm
for _, child := range f.AnonFuncs {
a.addFunction(child)
}
}
// Make a readable version of the method signature (including the function name, // Make a readable version of the method signature (including the function name,
// excluding the receiver name). This string is used internally to match // excluding the receiver name). This string is used internally to match
// interfaces and to call the correct method on an interface. Examples: // interfaces and to call the correct method on an interface. Examples:
// //
// String() string // String() string
// Read([]byte) (int, error) // Read([]byte) (int, error)
func (a *Analysis) MethodName(method *types.Func) string { func MethodName(method *types.Func) string {
sig := method.Type().(*types.Signature) sig := method.Type().(*types.Signature)
name := method.Name() name := method.Name()
if sig.Params().Len() == 0 { if sig.Params().Len() == 0 {
@ -161,15 +49,75 @@ func (a *Analysis) MethodName(method *types.Func) string {
// //
// All packages need to be added before this pass can run, or it will produce // All packages need to be added before this pass can run, or it will produce
// incorrect results. // incorrect results.
func (a *Analysis) AnalyseCallgraph() { func (p *Program) AnalyseCallgraph() {
for f, fm := range a.functions { for _, f := range p.Functions {
for _, child := range fm.children { // Clear, if AnalyseCallgraph has been called before.
childRes, ok := a.functions[child] f.children = nil
if !ok { f.parents = nil
println("child not found: " + child.Pkg.Pkg.Path() + "." + child.Name() + ", function: " + f.Name())
continue for _, block := range f.fn.Blocks {
for _, instr := range block.Instrs {
switch instr := instr.(type) {
case *ssa.Call:
if instr.Common().IsInvoke() {
continue
}
switch call := instr.Call.Value.(type) {
case *ssa.Builtin:
// ignore
case *ssa.Function:
if isCGoInternal(call.Name()) {
continue
}
child := p.GetFunction(call)
if child.CName() != "" {
continue // assume non-blocking
}
if child.Name(false) == "runtime.Sleep" {
f.blocking = true
}
f.children = append(f.children, child)
}
}
}
}
}
for _, f := range p.Functions {
for _, child := range f.children {
child.parents = append(child.parents, f)
}
}
}
// Find all types that are put in an interface.
func (p *Program) AnalyseInterfaceConversions() {
// Clear, if AnalyseTypes has been called before.
p.typesWithMethods = make(map[string]*InterfaceType)
p.typesWithoutMethods = make(map[string]int)
for _, f := range p.Functions {
for _, block := range f.fn.Blocks {
for _, instr := range block.Instrs {
switch instr := instr.(type) {
case *ssa.MakeInterface:
methods := getAllMethods(f.fn.Prog, instr.X.Type())
name := instr.X.Type().String()
if _, ok := p.typesWithMethods[name]; !ok && len(methods) > 0 {
t := &InterfaceType{
t: instr.X.Type(),
Num: len(p.typesWithMethods),
Methods: make(map[string]*types.Selection),
}
for _, sel := range methods {
name := MethodName(sel.Obj().(*types.Func))
t.Methods[name] = sel
}
p.typesWithMethods[instr.X.Type().String()] = t
} else if _, ok := p.typesWithoutMethods[name]; !ok && len(methods) == 0 {
p.typesWithoutMethods[name] = len(p.typesWithoutMethods)
}
}
} }
childRes.parents = append(childRes.parents, f)
} }
} }
} }
@ -177,13 +125,13 @@ func (a *Analysis) AnalyseCallgraph() {
// Analyse which functions are recursively blocking. // Analyse which functions are recursively blocking.
// //
// Depends on AnalyseCallgraph. // Depends on AnalyseCallgraph.
func (a *Analysis) AnalyseBlockingRecursive() { func (p *Program) AnalyseBlockingRecursive() {
worklist := make([]*FuncMeta, 0) worklist := make([]*Function, 0)
// Fill worklist with directly blocking functions. // Fill worklist with directly blocking functions.
for _, fm := range a.functions { for _, f := range p.Functions {
if fm.blocking { if f.blocking {
worklist = append(worklist, fm) worklist = append(worklist, f)
} }
} }
@ -193,13 +141,12 @@ func (a *Analysis) AnalyseBlockingRecursive() {
// The work items are then grey objects. // The work items are then grey objects.
for len(worklist) != 0 { for len(worklist) != 0 {
// Pick the topmost. // Pick the topmost.
fm := worklist[len(worklist)-1] f := worklist[len(worklist)-1]
worklist = worklist[:len(worklist)-1] worklist = worklist[:len(worklist)-1]
for _, parent := range fm.parents { for _, parent := range f.parents {
parentfm := a.functions[parent] if !parent.blocking {
if !parentfm.blocking { parent.blocking = true
parentfm.blocking = true worklist = append(worklist, parent)
worklist = append(worklist, parentfm)
} }
} }
} }
@ -210,10 +157,27 @@ func (a *Analysis) AnalyseBlockingRecursive() {
// function can be turned into a regular function call). // function can be turned into a regular function call).
// //
// Depends on AnalyseBlockingRecursive. // Depends on AnalyseBlockingRecursive.
func (a *Analysis) AnalyseGoCalls() { func (p *Program) AnalyseGoCalls() {
for _, instr := range a.goCalls { p.goCalls = nil
if a.isBlocking(instr.Call.Value) { for _, f := range p.Functions {
a.needsScheduler = true for _, block := range f.fn.Blocks {
for _, instr := range block.Instrs {
switch instr := instr.(type) {
case *ssa.Go:
p.goCalls = append(p.goCalls, instr)
}
}
}
}
for _, instr := range p.goCalls {
switch instr := instr.Call.Value.(type) {
case *ssa.Builtin:
case *ssa.Function:
if p.functionMap[instr].blocking {
p.needsScheduler = true
}
default:
panic("unknown go call function type")
} }
} }
} }
@ -221,30 +185,19 @@ func (a *Analysis) AnalyseGoCalls() {
// Whether this function needs a scheduler. // Whether this function needs a scheduler.
// //
// Depends on AnalyseGoCalls. // Depends on AnalyseGoCalls.
func (a *Analysis) NeedsScheduler() bool { func (p *Program) NeedsScheduler() bool {
return a.needsScheduler return p.needsScheduler
} }
// Whether this function blocks. Builtins are also accepted for convenience. // Whether this function blocks. Builtins are also accepted for convenience.
// They will always be non-blocking. // They will always be non-blocking.
// //
// Depends on AnalyseBlockingRecursive. // Depends on AnalyseBlockingRecursive.
func (a *Analysis) IsBlocking(f ssa.Value) bool { func (p *Program) IsBlocking(f *Function) bool {
if !a.needsScheduler { if !p.needsScheduler {
return false return false
} }
return a.isBlocking(f) return f.blocking
}
func (a *Analysis) isBlocking(f ssa.Value) bool {
switch f := f.(type) {
case *ssa.Builtin:
return false
case *ssa.Function:
return a.functions[f].blocking
default:
panic("Analysis.IsBlocking on unknown type")
}
} }
// Return the type number and whether this type is actually used. Used in // Return the type number and whether this type is actually used. Used in
@ -252,11 +205,11 @@ func (a *Analysis) isBlocking(f ssa.Value) bool {
// used, meaning assert is always false in this program). // used, meaning assert is always false in this program).
// //
// May only be used after all packages have been added to the analyser. // May only be used after all packages have been added to the analyser.
func (a *Analysis) TypeNum(typ types.Type) (int, bool) { func (p *Program) TypeNum(typ types.Type) (int, bool) {
if n, ok := a.typesWithoutMethods[typ.String()]; ok { if n, ok := p.typesWithoutMethods[typ.String()]; ok {
return n, true return n, true
} else if meta, ok := a.typesWithMethods[typ.String()]; ok { } else if meta, ok := p.typesWithMethods[typ.String()]; ok {
return len(a.typesWithoutMethods) + meta.Num, true return len(p.typesWithoutMethods) + meta.Num, true
} else { } else {
return -1, false // type is never put in an interface return -1, false // type is never put in an interface
} }
@ -264,11 +217,12 @@ func (a *Analysis) TypeNum(typ types.Type) (int, bool) {
// MethodNum returns the numeric ID of this method, to be used in method lookups // MethodNum returns the numeric ID of this method, to be used in method lookups
// on interfaces for example. // on interfaces for example.
func (a *Analysis) MethodNum(method *types.Func) int { func (p *Program) MethodNum(method *types.Func) int {
if n, ok := a.methodSignatureNames[a.MethodName(method)]; ok { name := MethodName(method)
return n if _, ok := p.methodSignatureNames[name]; !ok {
p.methodSignatureNames[name] = len(p.methodSignatureNames)
} }
return -1 // signal error return p.methodSignatureNames[MethodName(method)]
} }
// The start index of the first dynamic type that has methods. // The start index of the first dynamic type that has methods.
@ -276,14 +230,14 @@ func (a *Analysis) MethodNum(method *types.Func) int {
// or a higher ID. // or a higher ID.
// //
// May only be used after all packages have been added to the analyser. // May only be used after all packages have been added to the analyser.
func (a *Analysis) FirstDynamicType() int { func (p *Program) FirstDynamicType() int {
return len(a.typesWithoutMethods) return len(p.typesWithoutMethods)
} }
// Return all types with methods, sorted by type ID. // Return all types with methods, sorted by type ID.
func (a *Analysis) AllDynamicTypes() []*TypeMeta { func (p *Program) AllDynamicTypes() []*InterfaceType {
l := make([]*TypeMeta, len(a.typesWithMethods)) l := make([]*InterfaceType, len(p.typesWithMethods))
for _, m := range a.typesWithMethods { for _, m := range p.typesWithMethods {
l[m.Num] = m l[m.Num] = m
} }
return l return l

156
ir.go Обычный файл
Просмотреть файл

@ -0,0 +1,156 @@
package main
import (
"go/types"
"sort"
"strings"
"github.com/aykevl/llvm/bindings/go/llvm"
"golang.org/x/tools/go/ssa"
)
// View on all functions, types, and globals in a program, with analysis
// results.
type Program struct {
Functions []*Function
functionMap map[*ssa.Function]*Function
Globals []*Global
globalMap map[*ssa.Global]*Global
NamedTypes []*NamedType
needsScheduler bool
goCalls []*ssa.Go
typesWithMethods map[string]*InterfaceType
typesWithoutMethods map[string]int
methodSignatureNames map[string]int
}
// Function or method.
type Function struct {
fn *ssa.Function
llvmFn llvm.Value
blocking bool
parents []*Function // calculated by AnalyseCallgraph
children []*Function
}
// Global variable, possibly constant.
type Global struct {
g *ssa.Global
llvmGlobal llvm.Value
}
// Type with a name and possibly methods.
type NamedType struct {
t *ssa.Type
}
// Type that is at some point put in an interface.
type InterfaceType struct {
t types.Type
Num int
Methods map[string]*types.Selection
}
func NewProgram() *Program {
return &Program{
functionMap: make(map[*ssa.Function]*Function),
globalMap: make(map[*ssa.Global]*Global),
methodSignatureNames: make(map[string]int),
}
}
// Add a package to this Program. All packages need to be added first before any
// analysis is done for correct results.
func (p *Program) AddPackage(pkg *ssa.Package) {
memberNames := make([]string, 0)
for name := range pkg.Members {
if isCGoInternal(name) {
continue
}
memberNames = append(memberNames, name)
}
sort.Strings(memberNames)
for _, name := range memberNames {
member := pkg.Members[name]
switch member := member.(type) {
case *ssa.Function:
if isCGoInternal(member.Name()) {
continue
}
p.addFunction(member)
case *ssa.Type:
t := &NamedType{t: member}
p.NamedTypes = append(p.NamedTypes, t)
methods := getAllMethods(pkg.Prog, member.Type())
if !types.IsInterface(member.Type()) {
// named type
for _, method := range methods {
p.addFunction(pkg.Prog.MethodValue(method))
}
}
case *ssa.Global:
g := &Global{g: member}
p.Globals = append(p.Globals, g)
p.globalMap[member] = g
}
}
}
func (p *Program) addFunction(ssaFn *ssa.Function) {
f := &Function{fn: ssaFn}
p.Functions = append(p.Functions, f)
p.functionMap[ssaFn] = f
}
func (p *Program) GetFunction(ssaFn *ssa.Function) *Function {
return p.functionMap[ssaFn]
}
func (p *Program) GetGlobal(ssaGlobal *ssa.Global) *Global {
return p.globalMap[ssaGlobal]
}
// Return the link name for this function.
func (f *Function) Name(blocking bool) string {
suffix := ""
if blocking {
suffix = "$async"
}
if f.fn.Signature.Recv() != nil {
// Method on a defined type (which may be a pointer).
return f.fn.RelString(nil) + suffix
} else {
// Bare function.
if name := f.CName(); name != "" {
// Name CGo functions directly.
return name
} else {
name := f.fn.RelString(nil) + suffix
if f.fn.Pkg.Pkg.Path() == "runtime" && strings.HasPrefix(f.fn.Name(), "_llvm_") {
// Special case for LLVM intrinsics in the runtime.
name = "llvm." + strings.Replace(f.fn.Name()[len("_llvm_"):], "_", ".", -1)
}
return name
}
}
}
// Return the name of the C function if this is a CGo wrapper. Otherwise, return
// a zero-length string.
func (f *Function) CName() string {
name := f.fn.Name()
if strings.HasPrefix(name, "_Cfunc_") {
return name[len("_Cfunc_"):]
}
return ""
}
// Return the link name for this global.
func (g *Global) Name() string {
if strings.HasPrefix(g.g.Name(), "_extern_") {
return g.g.Name()[len("_extern_"):]
} else {
return g.g.RelString(nil)
}
}

392
tgo.go
Просмотреть файл

@ -10,7 +10,6 @@ import (
"go/token" "go/token"
"go/types" "go/types"
"os" "os"
"sort"
"strings" "strings"
"github.com/aykevl/llvm/bindings/go/llvm" "github.com/aykevl/llvm/bindings/go/llvm"
@ -50,12 +49,11 @@ type Compiler struct {
program *ssa.Program program *ssa.Program
mainPkg *ssa.Package mainPkg *ssa.Package
initFuncs []llvm.Value initFuncs []llvm.Value
analysis *Analysis ir *Program
} }
type Frame struct { type Frame struct {
fn *ssa.Function fn *Function
llvmFn llvm.Value
params map[*ssa.Parameter]int // arguments to the function params map[*ssa.Parameter]int // arguments to the function
locals map[ssa.Value]llvm.Value // local variables locals map[ssa.Value]llvm.Value // local variables
blocks map[*ssa.BasicBlock]llvm.BasicBlock blocks map[*ssa.BasicBlock]llvm.BasicBlock
@ -73,9 +71,9 @@ type Phi struct {
func NewCompiler(pkgName, triple string, dumpSSA bool) (*Compiler, error) { func NewCompiler(pkgName, triple string, dumpSSA bool) (*Compiler, error) {
c := &Compiler{ c := &Compiler{
dumpSSA: dumpSSA, dumpSSA: dumpSSA,
triple: triple, triple: triple,
analysis: NewAnalysis(), ir: NewProgram(),
} }
target, err := llvm.GetTargetFromTriple(triple) target, err := llvm.GetTargetFromTriple(triple)
@ -208,15 +206,91 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error {
} }
for _, pkg := range packageList { for _, pkg := range packageList {
c.analysis.AddPackage(pkg) c.ir.AddPackage(pkg)
} }
c.analysis.AnalyseCallgraph() // set up callgraph c.ir.AnalyseCallgraph() // set up callgraph
c.analysis.AnalyseBlockingRecursive() // make all parents of blocking calls blocking (transitively) c.ir.AnalyseInterfaceConversions() // determine which types are converted to an interface
c.analysis.AnalyseGoCalls() // check whether we need a scheduler c.ir.AnalyseBlockingRecursive() // make all parents of blocking calls blocking (transitively)
c.ir.AnalyseGoCalls() // check whether we need a scheduler
// Transform each package into LLVM IR. var frames []*Frame
for _, pkg := range packageList {
err := c.parsePackage(pkg) // Declare all named (struct) types.
for _, t := range c.ir.NamedTypes {
if named, ok := t.t.Type().(*types.Named); ok {
if st, ok := named.Underlying().(*types.Struct); ok {
llvmType, err := c.getLLVMType(st)
if err != nil {
return err
}
llvmNamedType := c.ctx.StructCreateNamed(named.Obj().Pkg().Path() + "." + named.Obj().Name())
llvmNamedType.StructSetBody(llvmType.StructElementTypes(), false)
}
}
}
// Declare all globals. These will get an initializer when parsing "package
// initializer" packages.
for _, g := range c.ir.Globals {
typ := g.g.Type()
if typPtr, ok := typ.(*types.Pointer); ok {
typ = typPtr.Elem()
} else {
return errors.New("global is not a pointer")
}
llvmType, err := c.getLLVMType(typ)
if err != nil {
return err
}
global := llvm.AddGlobal(c.mod, llvmType, g.Name())
g.llvmGlobal = global
if !strings.HasPrefix(g.Name(), "_extern_") {
global.SetLinkage(llvm.PrivateLinkage)
if g.Name() == "runtime.TargetBits" {
bitness := c.targetData.PointerSize() * 8
if bitness < 32 {
// Only 8 and 32+ architectures supported at the moment.
// On 8 bit architectures, pointers are normally bigger
// than 8 bits to do anything meaningful.
// TODO: clean up this hack to support 16-bit
// architectures.
bitness = 8
}
global.SetInitializer(llvm.ConstInt(llvm.Int8Type(), uint64(bitness), false))
global.SetGlobalConstant(true)
} else {
initializer, err := getZeroValue(llvmType)
if err != nil {
return err
}
global.SetInitializer(initializer)
}
}
}
// Declare all functions.
for _, f := range c.ir.Functions {
frame, err := c.parseFuncDecl(f)
if err != nil {
return err
}
frames = append(frames, frame)
}
// Add definitions to declarations.
for _, frame := range frames {
if frame.fn.CName() != "" {
continue
}
if frame.fn.fn.Blocks == nil {
continue // external function
}
var err error
if frame.fn.fn.Synthetic == "package initializer" {
err = c.parseInitFunc(frame)
} else {
err = c.parseFunc(frame)
}
if err != nil { if err != nil {
return err return err
} }
@ -254,13 +328,13 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error {
c.mod.NamedFunction("runtime.scheduler").SetLinkage(llvm.PrivateLinkage) c.mod.NamedFunction("runtime.scheduler").SetLinkage(llvm.PrivateLinkage)
// Only use a scheduler when necessary. // Only use a scheduler when necessary.
if c.analysis.NeedsScheduler() { if c.ir.NeedsScheduler() {
// Enable the scheduler. // Enable the scheduler.
c.mod.NamedGlobal("has_scheduler").SetInitializer(llvm.ConstInt(llvm.Int1Type(), 1, false)) c.mod.NamedGlobal("has_scheduler").SetInitializer(llvm.ConstInt(llvm.Int1Type(), 1, false))
} }
// Initialize runtime type information, for interfaces. // Initialize runtime type information, for interfaces.
dynamicTypes := c.analysis.AllDynamicTypes() dynamicTypes := c.ir.AllDynamicTypes()
numDynamicTypes := 0 numDynamicTypes := 0
for _, meta := range dynamicTypes { for _, meta := range dynamicTypes {
numDynamicTypes += len(meta.Methods) numDynamicTypes += len(meta.Methods)
@ -278,14 +352,13 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error {
tuple := llvm.ConstNamedStruct(tupleType, tupleValues) tuple := llvm.ConstNamedStruct(tupleType, tupleValues)
tuples = append(tuples, tuple) tuples = append(tuples, tuple)
for _, method := range meta.Methods { for _, method := range meta.Methods {
fnName := getFunctionName(c.program.MethodValue(method), false) f := c.ir.GetFunction(c.program.MethodValue(method))
llvmFn := c.mod.NamedFunction(fnName) if f.llvmFn.IsNil() {
if llvmFn.IsNil() { return errors.New("cannot find function: " + f.Name(false))
return errors.New("cannot find function: " + fnName)
} }
fn := llvm.ConstBitCast(llvmFn, c.i8ptrType) fn := llvm.ConstBitCast(f.llvmFn, c.i8ptrType)
funcPointers = append(funcPointers, fn) funcPointers = append(funcPointers, fn)
signatureNum := c.analysis.MethodNum(method.Obj().(*types.Func)) signatureNum := c.ir.MethodNum(method.Obj().(*types.Func))
signature := llvm.ConstInt(llvm.Int32Type(), uint64(signatureNum), false) signature := llvm.ConstInt(llvm.Int32Type(), uint64(signatureNum), false)
signatures = append(signatures, signature) signatures = append(signatures, signature)
} }
@ -314,7 +387,7 @@ func (c *Compiler) Parse(mainPath string, buildTags []string) error {
signatureArrayOldGlobal.EraseFromParentAsGlobal() signatureArrayOldGlobal.EraseFromParentAsGlobal()
signatureArrayNewGlobal.SetName("interface_signatures") signatureArrayNewGlobal.SetName("interface_signatures")
c.mod.NamedGlobal("first_interface_num").SetInitializer(llvm.ConstInt(llvm.Int32Type(), uint64(c.analysis.FirstDynamicType()), false)) c.mod.NamedGlobal("first_interface_num").SetInitializer(llvm.ConstInt(llvm.Int32Type(), uint64(c.ir.FirstDynamicType()), false))
return nil return nil
} }
@ -489,38 +562,6 @@ func getAllMethods(prog *ssa.Program, typ types.Type) []*types.Selection {
return methods return methods
} }
func getFunctionName(fn *ssa.Function, blocking bool) string {
suffix := ""
if blocking {
suffix = "$async"
}
if fn.Signature.Recv() != nil {
// Method on a defined type (which may be a pointer).
return fn.RelString(nil) + suffix
} else {
// Bare function.
if name := getCName(fn.Name()); name != "" {
// Name CGo functions directly.
return name
} else {
name := fn.RelString(nil) + suffix
if fn.Pkg.Pkg.Path() == "runtime" && strings.HasPrefix(fn.Name(), "_llvm_") {
// Special case for LLVM intrinsics in the runtime.
name = "llvm." + strings.Replace(fn.Name()[len("_llvm_"):], "_", ".", -1)
}
return name
}
}
}
func getGlobalName(global *ssa.Global) string {
if strings.HasPrefix(global.Name(), "_extern_") {
return global.Name()[len("_extern_"):]
} else {
return global.RelString(nil)
}
}
// Return true if this is a CGo-internal function that can be ignored. // Return true if this is a CGo-internal function that can be ignored.
func isCGoInternal(name string) bool { func isCGoInternal(name string) bool {
if strings.HasPrefix(name, "_Cgo_") || strings.HasPrefix(name, "_cgo") { if strings.HasPrefix(name, "_Cgo_") || strings.HasPrefix(name, "_cgo") {
@ -533,180 +574,26 @@ func isCGoInternal(name string) bool {
return false return false
} }
// Return the name of the C function if this is a CGo call. Otherwise, return a func (c *Compiler) parseFuncDecl(f *Function) (*Frame, error) {
// zero-length string.
func getCName(name string) string {
if strings.HasPrefix(name, "_Cfunc_") {
return name[len("_Cfunc_"):]
}
return ""
}
func (c *Compiler) parsePackage(pkg *ssa.Package) error {
// Make sure we're walking through all members in a constant order every
// run, and skip cgo wrapper functions/globals which we don't need.
memberNames := make([]string, 0)
for name := range pkg.Members {
if isCGoInternal(name) {
continue
}
memberNames = append(memberNames, name)
}
sort.Strings(memberNames)
frames := make(map[*ssa.Function]*Frame)
// First, declare all named (struct) types.
for _, name := range memberNames {
member := pkg.Members[name]
switch member := member.(type) {
case *ssa.Type:
if named, ok := member.Type().(*types.Named); ok {
if st, ok := named.Underlying().(*types.Struct); ok {
llvmType, err := c.getLLVMType(st)
if err != nil {
return err
}
llvmNamedType := c.ctx.StructCreateNamed(named.Obj().Pkg().Path() + "." + named.Obj().Name())
llvmNamedType.StructSetBody(llvmType.StructElementTypes(), false)
}
}
}
}
// With the types defined, build all function declarations.
for _, name := range memberNames {
member := pkg.Members[name]
switch member := member.(type) {
case *ssa.Function:
frame, err := c.parseFuncDecl(member)
if err != nil {
return err
}
frames[member] = frame
if member.Synthetic == "package initializer" {
c.initFuncs = append(c.initFuncs, frame.llvmFn)
}
// TODO: recursively anonymous functions
for _, child := range member.AnonFuncs {
frame, err := c.parseFuncDecl(child)
if err != nil {
return err
}
frames[child] = frame
}
case *ssa.NamedConst:
// Ignore package-level untyped constants. The SSA form doesn't need
// them.
case *ssa.Global:
typ := member.Type()
if typPtr, ok := typ.(*types.Pointer); ok {
typ = typPtr.Elem()
} else {
return errors.New("global is not a pointer")
}
llvmType, err := c.getLLVMType(typ)
if err != nil {
return err
}
global := llvm.AddGlobal(c.mod, llvmType, getGlobalName(member))
if !strings.HasPrefix(member.Name(), "_extern_") {
global.SetLinkage(llvm.PrivateLinkage)
if getGlobalName(member) == "runtime.TargetBits" {
bitness := c.targetData.PointerSize() * 8
if bitness < 32 {
// Only 8 and 32+ architectures supported at the moment.
// On 8 bit architectures, pointers are normally bigger
// than 8 bits to do anything meaningful.
// TODO: clean up this hack to support 16-bit
// architectures.
bitness = 8
}
global.SetInitializer(llvm.ConstInt(llvm.Int8Type(), uint64(bitness), false))
global.SetGlobalConstant(true)
} else {
initializer, err := getZeroValue(llvmType)
if err != nil {
return err
}
global.SetInitializer(initializer)
}
}
case *ssa.Type:
if !types.IsInterface(member.Type()) {
for _, sel := range getAllMethods(c.program, member.Type()) {
fn := c.program.MethodValue(sel)
frame, err := c.parseFuncDecl(fn)
if err != nil {
return err
}
frames[fn] = frame
}
}
default:
return errors.New("todo: member: " + fmt.Sprintf("%#v", member))
}
}
// Now, add definitions to those declarations.
for _, name := range memberNames {
member := pkg.Members[name]
switch member := member.(type) {
case *ssa.Function:
if getCName(name) != "" {
// CGo function. Don't implement it's body.
continue
}
if member.Blocks == nil {
continue // external function
}
var err error
if member.Synthetic == "package initializer" {
err = c.parseInitFunc(frames[member], member)
} else {
err = c.parseFunc(frames[member], member)
}
if err != nil {
return err
}
case *ssa.Type:
if !types.IsInterface(member.Type()) {
for _, sel := range getAllMethods(c.program, member.Type()) {
fn := c.program.MethodValue(sel)
err := c.parseFunc(frames[fn], fn)
if err != nil {
return err
}
}
}
}
}
return nil
}
func (c *Compiler) parseFuncDecl(f *ssa.Function) (*Frame, error) {
frame := &Frame{ frame := &Frame{
fn: f, fn: f,
params: make(map[*ssa.Parameter]int), params: make(map[*ssa.Parameter]int),
locals: make(map[ssa.Value]llvm.Value), locals: make(map[ssa.Value]llvm.Value),
blocks: make(map[*ssa.BasicBlock]llvm.BasicBlock), blocks: make(map[*ssa.BasicBlock]llvm.BasicBlock),
blocking: c.analysis.IsBlocking(f), blocking: c.ir.IsBlocking(f),
} }
var retType llvm.Type var retType llvm.Type
if frame.blocking { if frame.blocking {
if f.Signature.Results() != nil { if f.fn.Signature.Results() != nil {
return nil, errors.New("todo: return values in blocking function") return nil, errors.New("todo: return values in blocking function")
} }
retType = c.i8ptrType retType = c.i8ptrType
} else if f.Signature.Results() == nil { } else if f.fn.Signature.Results() == nil {
retType = llvm.VoidType() retType = llvm.VoidType()
} else if f.Signature.Results().Len() == 1 { } else if f.fn.Signature.Results().Len() == 1 {
var err error var err error
retType, err = c.getLLVMType(f.Signature.Results().At(0).Type()) retType, err = c.getLLVMType(f.fn.Signature.Results().At(0).Type())
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -718,7 +605,7 @@ func (c *Compiler) parseFuncDecl(f *ssa.Function) (*Frame, error) {
if frame.blocking { if frame.blocking {
paramTypes = append(paramTypes, c.i8ptrType) // parent coroutine paramTypes = append(paramTypes, c.i8ptrType) // parent coroutine
} }
for i, param := range f.Params { for i, param := range f.fn.Params {
paramType, err := c.getLLVMType(param.Type()) paramType, err := c.getLLVMType(param.Type())
if err != nil { if err != nil {
return nil, err return nil, err
@ -729,22 +616,22 @@ func (c *Compiler) parseFuncDecl(f *ssa.Function) (*Frame, error) {
fnType := llvm.FunctionType(retType, paramTypes, false) fnType := llvm.FunctionType(retType, paramTypes, false)
name := getFunctionName(f, frame.blocking) name := f.Name(frame.blocking)
frame.llvmFn = c.mod.NamedFunction(name) frame.fn.llvmFn = c.mod.NamedFunction(name)
if frame.llvmFn.IsNil() { if frame.fn.llvmFn.IsNil() {
frame.llvmFn = llvm.AddFunction(c.mod, name, fnType) frame.fn.llvmFn = llvm.AddFunction(c.mod, name, fnType)
} }
return frame, nil return frame, nil
} }
// Special function parser for generated package initializers (which also // Special function parser for generated package initializers (which also
// initializes global variables). // initializes global variables).
func (c *Compiler) parseInitFunc(frame *Frame, f *ssa.Function) error { func (c *Compiler) parseInitFunc(frame *Frame) error {
frame.llvmFn.SetLinkage(llvm.PrivateLinkage) frame.fn.llvmFn.SetLinkage(llvm.PrivateLinkage)
llvmBlock := c.ctx.AddBasicBlock(frame.llvmFn, "entry") llvmBlock := c.ctx.AddBasicBlock(frame.fn.llvmFn, "entry")
c.builder.SetInsertPointAtEnd(llvmBlock) c.builder.SetInsertPointAtEnd(llvmBlock)
for _, block := range f.DomPreorder() { for _, block := range frame.fn.fn.DomPreorder() {
for _, instr := range block.Instrs { for _, instr := range block.Instrs {
var err error var err error
switch instr := instr.(type) { switch instr := instr.(type) {
@ -766,7 +653,7 @@ func (c *Compiler) parseInitFunc(frame *Frame, f *ssa.Function) error {
if err != nil { if err != nil {
return err return err
} }
llvmAddr := c.mod.NamedGlobal(getGlobalName(addr)) llvmAddr := c.ir.GetGlobal(addr).llvmGlobal
llvmAddr.SetInitializer(val) llvmAddr.SetInitializer(val)
case *ssa.FieldAddr: case *ssa.FieldAddr:
// Initialize field of a global struct. // Initialize field of a global struct.
@ -779,7 +666,7 @@ func (c *Compiler) parseInitFunc(frame *Frame, f *ssa.Function) error {
return err return err
} }
global := addr.X.(*ssa.Global) global := addr.X.(*ssa.Global)
llvmAddr := c.mod.NamedGlobal(getGlobalName(global)) llvmAddr := c.ir.GetGlobal(global).llvmGlobal
llvmValue := llvmAddr.Initializer() llvmValue := llvmAddr.Initializer()
if llvmValue.IsNil() { if llvmValue.IsNil() {
llvmValue, err = getZeroValue(llvmAddr.Type().ElementType()) llvmValue, err = getZeroValue(llvmAddr.Type().ElementType())
@ -801,7 +688,7 @@ func (c *Compiler) parseInitFunc(frame *Frame, f *ssa.Function) error {
} }
fieldAddr := addr.X.(*ssa.FieldAddr) fieldAddr := addr.X.(*ssa.FieldAddr)
global := fieldAddr.X.(*ssa.Global) global := fieldAddr.X.(*ssa.Global)
llvmAddr := c.mod.NamedGlobal(getGlobalName(global)) llvmAddr := c.ir.GetGlobal(global).llvmGlobal
llvmValue := llvmAddr.Initializer() llvmValue := llvmAddr.Initializer()
if llvmValue.IsNil() { if llvmValue.IsNil() {
llvmValue, err = getZeroValue(llvmAddr.Type().ElementType()) llvmValue, err = getZeroValue(llvmAddr.Type().ElementType())
@ -827,31 +714,31 @@ func (c *Compiler) parseInitFunc(frame *Frame, f *ssa.Function) error {
return nil return nil
} }
func (c *Compiler) parseFunc(frame *Frame, f *ssa.Function) error { func (c *Compiler) parseFunc(frame *Frame) error {
if c.dumpSSA { if c.dumpSSA {
fmt.Printf("\nfunc %s:\n", f) fmt.Printf("\nfunc %s:\n", frame.fn.fn)
} }
frame.llvmFn.SetLinkage(llvm.PrivateLinkage) frame.fn.llvmFn.SetLinkage(llvm.PrivateLinkage)
// Pre-create all basic blocks in the function. // Pre-create all basic blocks in the function.
for _, block := range f.DomPreorder() { for _, block := range frame.fn.fn.DomPreorder() {
llvmBlock := c.ctx.AddBasicBlock(frame.llvmFn, block.Comment) llvmBlock := c.ctx.AddBasicBlock(frame.fn.llvmFn, block.Comment)
frame.blocks[block] = llvmBlock frame.blocks[block] = llvmBlock
} }
if frame.blocking { if frame.blocking {
frame.cleanupBlock = c.ctx.AddBasicBlock(frame.llvmFn, "task.cleanup") frame.cleanupBlock = c.ctx.AddBasicBlock(frame.fn.llvmFn, "task.cleanup")
frame.suspendBlock = c.ctx.AddBasicBlock(frame.llvmFn, "task.suspend") frame.suspendBlock = c.ctx.AddBasicBlock(frame.fn.llvmFn, "task.suspend")
} }
// Load function parameters // Load function parameters
for _, param := range f.Params { for _, param := range frame.fn.fn.Params {
llvmParam := frame.llvmFn.Param(frame.params[param]) llvmParam := frame.fn.llvmFn.Param(frame.params[param])
frame.locals[param] = llvmParam frame.locals[param] = llvmParam
} }
if frame.blocking { if frame.blocking {
// Coroutine initialization. // Coroutine initialization.
c.builder.SetInsertPointAtEnd(frame.blocks[f.Blocks[0]]) c.builder.SetInsertPointAtEnd(frame.blocks[frame.fn.fn.Blocks[0]])
taskState := c.builder.CreateAlloca(c.mod.GetTypeByName("runtime.taskState"), "task.state") taskState := c.builder.CreateAlloca(c.mod.GetTypeByName("runtime.taskState"), "task.state")
stateI8 := c.builder.CreateBitCast(taskState, c.i8ptrType, "task.state.i8") stateI8 := c.builder.CreateBitCast(taskState, c.i8ptrType, "task.state.i8")
id := c.builder.CreateCall(c.coroIdFunc, []llvm.Value{ id := c.builder.CreateCall(c.coroIdFunc, []llvm.Value{
@ -874,7 +761,7 @@ func (c *Compiler) parseFunc(frame *Frame, f *ssa.Function) error {
mem := c.builder.CreateCall(c.coroFreeFunc, []llvm.Value{id, frame.taskHandle}, "task.data.free") mem := c.builder.CreateCall(c.coroFreeFunc, []llvm.Value{id, frame.taskHandle}, "task.data.free")
c.builder.CreateCall(c.freeFunc, []llvm.Value{mem}, "") c.builder.CreateCall(c.freeFunc, []llvm.Value{mem}, "")
// re-insert parent coroutine // re-insert parent coroutine
c.builder.CreateCall(c.mod.NamedFunction("runtime.scheduleTask"), []llvm.Value{frame.llvmFn.FirstParam()}, "") c.builder.CreateCall(c.mod.NamedFunction("runtime.scheduleTask"), []llvm.Value{frame.fn.llvmFn.FirstParam()}, "")
c.builder.CreateBr(frame.suspendBlock) c.builder.CreateBr(frame.suspendBlock)
// Coroutine suspend. A call to llvm.coro.suspend() will branch here. // Coroutine suspend. A call to llvm.coro.suspend() will branch here.
@ -884,7 +771,7 @@ func (c *Compiler) parseFunc(frame *Frame, f *ssa.Function) error {
} }
// Fill blocks with instructions. // Fill blocks with instructions.
for _, block := range f.DomPreorder() { for _, block := range frame.fn.fn.DomPreorder() {
if c.dumpSSA { if c.dumpSSA {
fmt.Printf("%s:\n", block.Comment) fmt.Printf("%s:\n", block.Comment)
} }
@ -933,7 +820,7 @@ func (c *Compiler) parseInstr(frame *Frame, instr ssa.Instruction) error {
// Execute non-blocking calls (including builtins) directly. // Execute non-blocking calls (including builtins) directly.
// parentHandle param is ignored. // parentHandle param is ignored.
if !c.analysis.IsBlocking(instr.Common().Value) { if !c.ir.IsBlocking(c.ir.GetFunction(instr.Common().Value.(*ssa.Function))) {
_, err := c.parseCall(frame, instr.Common(), llvm.Value{}) _, err := c.parseCall(frame, instr.Common(), llvm.Value{})
return err // probably nil return err // probably nil
} }
@ -1174,7 +1061,7 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon, parentHandle l
} }
values := []llvm.Value{ values := []llvm.Value{
itf, itf,
llvm.ConstInt(llvm.Int32Type(), uint64(c.analysis.MethodNum(instr.Method)), false), llvm.ConstInt(llvm.Int32Type(), uint64(c.ir.MethodNum(instr.Method)), false),
} }
fn := c.builder.CreateCall(c.mod.NamedFunction("itfmethod"), values, "invoke.func") fn := c.builder.CreateCall(c.mod.NamedFunction("itfmethod"), values, "invoke.func")
fnCast := c.builder.CreateBitCast(fn, llvmFnType, "invoke.func.cast") fnCast := c.builder.CreateBitCast(fn, llvmFnType, "invoke.func.cast")
@ -1206,11 +1093,11 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon, parentHandle l
} }
} }
targetBlocks := false targetBlocks := false
name := getFunctionName(call, targetBlocks) name := c.ir.GetFunction(call).Name(targetBlocks)
llvmFn := c.mod.NamedFunction(name) llvmFn := c.mod.NamedFunction(name)
if llvmFn.IsNil() { if llvmFn.IsNil() {
targetBlocks = true targetBlocks = true
nameAsync := getFunctionName(call, targetBlocks) nameAsync := c.ir.GetFunction(call).Name(targetBlocks)
llvmFn = c.mod.NamedFunction(nameAsync) llvmFn = c.mod.NamedFunction(nameAsync)
if llvmFn.IsNil() { if llvmFn.IsNil() {
return llvm.Value{}, errors.New("undefined function: " + name) return llvm.Value{}, errors.New("undefined function: " + name)
@ -1297,12 +1184,11 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
} }
return c.builder.CreateGEP(val, indices, ""), nil return c.builder.CreateGEP(val, indices, ""), nil
case *ssa.Function: case *ssa.Function:
return c.mod.NamedFunction(getFunctionName(expr, false)), nil return c.mod.NamedFunction(c.ir.GetFunction(expr).Name(false)), nil
case *ssa.Global: case *ssa.Global:
fullName := getGlobalName(expr) value := c.ir.GetGlobal(expr).llvmGlobal
value := c.mod.NamedGlobal(fullName)
if value.IsNil() { if value.IsNil() {
return llvm.Value{}, errors.New("global not found: " + fullName) return llvm.Value{}, errors.New("global not found: " + c.ir.GetGlobal(expr).Name())
} }
return value, nil return value, nil
case *ssa.IndexAddr: case *ssa.IndexAddr:
@ -1365,7 +1251,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
// Bounds check. // Bounds check.
// LLVM optimizes this away in most cases. // LLVM optimizes this away in most cases.
if frame.llvmFn.Name() != "runtime.boundsCheck" { if frame.fn.llvmFn.Name() != "runtime.boundsCheck" {
constZero := llvm.ConstInt(c.intType, 0, false) constZero := llvm.ConstInt(c.intType, 0, false)
isNegative := c.builder.CreateICmp(llvm.IntSLT, index, constZero, "") // index < 0 isNegative := c.builder.CreateICmp(llvm.IntSLT, index, constZero, "") // index < 0
strlen, err := c.parseBuiltin(frame, []ssa.Value{expr.X}, "len") strlen, err := c.parseBuiltin(frame, []ssa.Value{expr.X}, "len")
@ -1414,7 +1300,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
return llvm.Value{}, errors.New("todo: makeinterface: cast small type to i8*") return llvm.Value{}, errors.New("todo: makeinterface: cast small type to i8*")
} }
} }
itfTypeNum, _ := c.analysis.TypeNum(expr.X.Type()) itfTypeNum, _ := c.ir.TypeNum(expr.X.Type())
itf := llvm.ConstNamedStruct(c.mod.GetTypeByName("interface"), []llvm.Value{llvm.ConstInt(llvm.Int32Type(), uint64(itfTypeNum), false), llvm.Undef(c.i8ptrType)}) itf := llvm.ConstNamedStruct(c.mod.GetTypeByName("interface"), []llvm.Value{llvm.ConstInt(llvm.Int32Type(), uint64(itfTypeNum), false), llvm.Undef(c.i8ptrType)})
itf = c.builder.CreateInsertValue(itf, itfValue, 1, "") itf = c.builder.CreateInsertValue(itf, itfValue, 1, "")
return itf, nil return itf, nil
@ -1438,7 +1324,7 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
if err != nil { if err != nil {
return llvm.Value{}, err return llvm.Value{}, err
} }
assertedTypeNum, typeExists := c.analysis.TypeNum(expr.AssertedType) assertedTypeNum, typeExists := c.ir.TypeNum(expr.AssertedType)
if !typeExists { if !typeExists {
// Static analysis has determined this type assert will never apply. // Static analysis has determined this type assert will never apply.
return llvm.ConstStruct([]llvm.Value{llvm.Undef(assertedType), llvm.ConstInt(llvm.Int1Type(), 0, false)}, false), nil return llvm.ConstStruct([]llvm.Value{llvm.Undef(assertedType), llvm.ConstInt(llvm.Int1Type(), 0, false)}, false), nil
@ -1473,8 +1359,8 @@ func (c *Compiler) parseExpr(frame *Frame, expr ssa.Value) (llvm.Value, error) {
// interface. // interface.
commaOk := c.builder.CreateICmp(llvm.IntEQ, llvm.ConstInt(llvm.Int32Type(), uint64(assertedTypeNum), false), actualTypeNum, "") commaOk := c.builder.CreateICmp(llvm.IntEQ, llvm.ConstInt(llvm.Int32Type(), uint64(assertedTypeNum), false), actualTypeNum, "")
tuple := llvm.ConstStruct([]llvm.Value{llvm.Undef(assertedType), llvm.Undef(llvm.Int1Type())}, false) // create empty tuple tuple := llvm.ConstStruct([]llvm.Value{llvm.Undef(assertedType), llvm.Undef(llvm.Int1Type())}, false) // create empty tuple
tuple = c.builder.CreateInsertValue(tuple, value, 0, "") // insert value tuple = c.builder.CreateInsertValue(tuple, value, 0, "") // insert value
tuple = c.builder.CreateInsertValue(tuple, commaOk, 1, "") // insert 'comma ok' boolean tuple = c.builder.CreateInsertValue(tuple, commaOk, 1, "") // insert 'comma ok' boolean
return tuple, nil return tuple, nil
case *ssa.UnOp: case *ssa.UnOp:
return c.parseUnOp(frame, expr) return c.parseUnOp(frame, expr)
@ -1682,7 +1568,7 @@ func (c *Compiler) parseUnOp(frame *Frame, unop *ssa.UnOp) (llvm.Value, error) {
// Magic type name: treat the value as a register pointer. // Magic type name: treat the value as a register pointer.
register := unop.X.(*ssa.FieldAddr) register := unop.X.(*ssa.FieldAddr)
global := register.X.(*ssa.Global) global := register.X.(*ssa.Global)
llvmGlobal := c.mod.NamedGlobal(getGlobalName(global)) llvmGlobal := c.ir.GetGlobal(global).llvmGlobal
llvmAddr := c.builder.CreateExtractValue(llvmGlobal.Initializer(), register.Field, "") llvmAddr := c.builder.CreateExtractValue(llvmGlobal.Initializer(), register.Field, "")
ptr := llvm.ConstIntToPtr(llvmAddr, x.Type()) ptr := llvm.ConstIntToPtr(llvmAddr, x.Type())
load := c.builder.CreateLoad(ptr, "") load := c.builder.CreateLoad(ptr, "")