func (v *Codegen) functionTypeToLLVMType(typ parser.FunctionType, ptr bool) llvm.Type { numOfParams := len(typ.Parameters) if typ.Receiver != nil { numOfParams++ } params := make([]llvm.Type, 0, numOfParams) if typ.Receiver != nil { params = append(params, v.typeToLLVMType(typ.Receiver)) } for _, par := range typ.Parameters { params = append(params, v.typeToLLVMType(par)) } var returnType llvm.Type // oo theres a type, let's try figure it out if typ.Return != nil { returnType = v.typeToLLVMType(typ.Return) } else { returnType = llvm.VoidType() } // create the function type funcType := llvm.FunctionType(returnType, params, typ.IsVariadic) if ptr { funcType = llvm.PointerType(funcType, 0) } return funcType }
func (v *Codegen) primitiveTypeToLLVMType(typ parser.PrimitiveType) llvm.Type { switch typ { case parser.PRIMITIVE_int, parser.PRIMITIVE_uint: return v.targetData.IntPtrType() case parser.PRIMITIVE_s8, parser.PRIMITIVE_u8: return llvm.IntType(8) case parser.PRIMITIVE_s16, parser.PRIMITIVE_u16: return llvm.IntType(16) case parser.PRIMITIVE_s32, parser.PRIMITIVE_u32: return llvm.IntType(32) case parser.PRIMITIVE_s64, parser.PRIMITIVE_u64: return llvm.IntType(64) case parser.PRIMITIVE_s128, parser.PRIMITIVE_u128: return llvm.IntType(128) case parser.PRIMITIVE_f32: return llvm.FloatType() case parser.PRIMITIVE_f64: return llvm.DoubleType() case parser.PRIMITIVE_f128: return llvm.FP128Type() case parser.PRIMITIVE_rune: // runes are signed 32-bit int return llvm.IntType(32) case parser.PRIMITIVE_bool: return llvm.IntType(1) case parser.PRIMITIVE_void: return llvm.VoidType() default: panic("Unimplemented primitive type in LLVM codegen") } }
// emitInitPrologue emits the init-specific function prologue (guard check and // initialization of dependent packages under the llgo native ABI), and returns // the basic block into which the GC registration call should be emitted. func (fr *frame) emitInitPrologue() llvm.BasicBlock { if fr.GccgoABI { return fr.builder.GetInsertBlock() } initGuard := llvm.AddGlobal(fr.module.Module, llvm.Int1Type(), "init$guard") initGuard.SetLinkage(llvm.InternalLinkage) initGuard.SetInitializer(llvm.ConstNull(llvm.Int1Type())) returnBlock := llvm.AddBasicBlock(fr.function, "") initBlock := llvm.AddBasicBlock(fr.function, "") initGuardVal := fr.builder.CreateLoad(initGuard, "") fr.builder.CreateCondBr(initGuardVal, returnBlock, initBlock) fr.builder.SetInsertPointAtEnd(returnBlock) fr.builder.CreateRetVoid() fr.builder.SetInsertPointAtEnd(initBlock) fr.builder.CreateStore(llvm.ConstInt(llvm.Int1Type(), 1, false), initGuard) int8ptr := llvm.PointerType(fr.types.ctx.Int8Type(), 0) ftyp := llvm.FunctionType(llvm.VoidType(), []llvm.Type{int8ptr}, false) for _, pkg := range fr.pkg.Object.Imports() { initname := ManglePackagePath(pkg.Path()) + "..import" initfn := fr.module.Module.NamedFunction(initname) if initfn.IsNil() { initfn = llvm.AddFunction(fr.module.Module, initname, ftyp) } args := []llvm.Value{llvm.Undef(int8ptr)} fr.builder.CreateCall(initfn, args, "") } return initBlock }
func (c *compiler) createInitMainFunction(mainPkg *ssa.Package) { int8ptr := llvm.PointerType(c.types.ctx.Int8Type(), 0) ftyp := llvm.FunctionType(llvm.VoidType(), []llvm.Type{int8ptr}, false) initMain := llvm.AddFunction(c.module.Module, "__go_init_main", ftyp) c.addCommonFunctionAttrs(initMain) entry := llvm.AddBasicBlock(initMain, "entry") builder := llvm.GlobalContext().NewBuilder() defer builder.Dispose() builder.SetInsertPointAtEnd(entry) args := []llvm.Value{llvm.Undef(int8ptr)} if !c.GccgoABI { initfn := c.module.Module.NamedFunction("main..import") if !initfn.IsNil() { builder.CreateCall(initfn, args, "") } builder.CreateRetVoid() return } initdata := c.buildPackageInitData(mainPkg) for _, init := range initdata.Inits { initfn := c.module.Module.NamedFunction(init.InitFunc) if initfn.IsNil() { initfn = llvm.AddFunction(c.module.Module, init.InitFunc, ftyp) } builder.CreateCall(initfn, args, "") } builder.CreateRetVoid() }
func (v *Codegen) declareFunctionDecl(n *parser.FunctionDecl) { mangledName := n.Function.MangledName(parser.MANGLE_ARK_UNSTABLE) function := v.curFile.Module.NamedFunction(mangledName) if !function.IsNil() { v.err("function `%s` already exists in module", n.Function.Name) } else { numOfParams := len(n.Function.Parameters) if n.Function.IsMethod && !n.Function.IsStatic { numOfParams++ } params := make([]llvm.Type, 0, numOfParams) if n.Function.IsMethod && !n.Function.IsStatic { params = append(params, v.typeToLLVMType(n.Function.Receiver.Variable.Type)) } for _, par := range n.Function.Parameters { params = append(params, v.typeToLLVMType(par.Variable.Type)) } // attributes defaults cBinding := false // find them attributes yo if n.Function.Attrs != nil { cBinding = n.Function.Attrs.Contains("c") } // assume it's void funcTypeRaw := llvm.VoidType() // oo theres a type, let's try figure it out if n.Function.ReturnType != nil { funcTypeRaw = v.typeToLLVMType(n.Function.ReturnType) } // create the function type funcType := llvm.FunctionType(funcTypeRaw, params, n.Function.IsVariadic) functionName := mangledName if cBinding { functionName = n.Function.Name } // add that shit function = llvm.AddFunction(v.curFile.Module, functionName, funcType) /*// do some magical shit for later for i := 0; i < numOfParams; i++ { funcParam := function.Param(i) funcParam.SetName(n.Function.Parameters[i].Variable.MangledName(parser.MANGLE_ARK_UNSTABLE)) }*/ if cBinding { function.SetFunctionCallConv(llvm.CCallConv) } else { function.SetFunctionCallConv(llvm.FastCallConv) } } }
func (c *Codegen) getLLVMType(node parser.Node) llvm.Type { switch t := node.(type) { /* case *FuncTypeNode: case *ArrayTypeNode: */ case *parser.NamedTypeNode: name := t.Name.Value if prim, ok := PRIMITIVE_TYPES[name]; ok { return prim } if t, ok := c.templates[name]; ok { return llvm.PointerType(t.Type, 0) } case *parser.BinaryExprNode: return c.getLLVMType(t.Left) case *parser.CharLitNode: return PRIMITIVE_TYPES["char"] case *parser.BoolLitNode: return PRIMITIVE_TYPES["boolean"] case *parser.NumLitNode: if t.IsFloat { return PRIMITIVE_TYPES["float"] } else { return PRIMITIVE_TYPES["int"] } case *parser.StringLitNode: return llvm.PointerType(c.templates["string"].Type, 0) case *parser.VarAccessNode: if param := c.getCurrParam(t.Name.Value); !param.IsNil() { return param.Type() } else if t := c.scope.GetValue(t.Name.Value).Type(); t != llvm.VoidType() { return t } case *parser.ObjectAccessNode: obj := c.getLLVMType(t.Object) tmpl := c.templates[c.getStructFromPointer(obj)] return c.getLLVMType(tmpl.Values[tmpl.Variables[t.Member.Value]].Type) case *parser.CallExprNode: return c.getLLVMTypeOfCall(t) } return llvm.VoidType() }
func (c *Codegen) declareMemcpy() { t := llvm.FunctionType(llvm.VoidType(), []llvm.Type{ llvm.PointerType(PRIMITIVE_TYPES["char"], 0), llvm.PointerType(PRIMITIVE_TYPES["char"], 0), PRIMITIVE_TYPES["int"], PRIMITIVE_TYPES["int"], PRIMITIVE_TYPES["boolean"], }, false) llvm.AddFunction(c.module, "llvm.memcpy.p0i8.p0i8.i32", t) }
func (v *Codegen) genFunctionDecl(n *parser.FunctionDecl) llvm.Value { var res llvm.Value mangledName := n.Function.MangledName(parser.MANGLE_ARK_UNSTABLE) function := v.curFile.Module.NamedFunction(mangledName) if function.IsNil() { //v.err("genning function `%s` doesn't exist in module", n.Function.Name) // hmmmm seems we just ignore this here } else { if !n.Prototype { block := llvm.AddBasicBlock(function, "entry") v.builder.SetInsertPointAtEnd(block) for i, par := range n.Function.Parameters { alloc := v.builder.CreateAlloca(v.typeToLLVMType(par.Variable.Type), par.Variable.MangledName(parser.MANGLE_ARK_UNSTABLE)) v.variableLookup[par.Variable] = alloc v.builder.CreateStore(function.Params()[i], alloc) } v.inFunction = true v.currentFunction = function for _, stat := range n.Function.Body.Nodes { v.genNode(stat) } v.inFunction = false retType := llvm.VoidType() if n.Function.ReturnType != nil { retType = v.typeToLLVMType(n.Function.ReturnType) } // function returns void, lets return void // unless its a prototype obviously... if retType == llvm.VoidType() && !n.Prototype { v.builder.CreateRetVoid() } } } return res }
func (c *Codegen) getLLVMTypeOfCall(node *parser.CallExprNode) llvm.Type { switch t := node.Function.(type) { case *parser.VarAccessNode: return c.module.NamedFunction(t.Name.Value).Type().ReturnType() case *parser.ObjectAccessNode: tmpl := c.getStructFromPointer(c.getLLVMType(t.Object)) return c.module.NamedFunction("-" + tmpl + "-" + t.Member.Value).Type().ElementType().ReturnType() } return llvm.VoidType() }
func (c *Codegen) getLLVMFuncType(ret parser.Node, params []*parser.VarDeclNode, obj llvm.Type) llvm.Type { p := make([]llvm.Type, 0) if obj != llvm.VoidType() { p = append(p, obj) } for _, v := range params { p = append(p, c.getLLVMType(v.Type)) } return llvm.FunctionType(c.getLLVMType(ret), p, false) }
func (c *Codegen) declareFunc(n *parser.FuncNode, obj llvm.Type) { sig := n.Signature name := c.mangle(sig.Name.Value) f := c.getLLVMFuncType(sig.Return, sig.Parameters, obj) llvmf := llvm.AddFunction(c.module, name, f) if obj != llvm.VoidType() { llvmf.Param(0).SetName("this") } block := llvm.AddBasicBlock(c.module.NamedFunction(name), "entry") c.functions[name] = block }
func (c *Codegen) declareTopLevelNodes() { for _, node := range c.tree.Nodes { switch n := node.(type) { case *parser.TemplateNode: c.presetTemplate(n) } } for _, node := range c.tree.Nodes { switch n := node.(type) { case *parser.FuncDeclNode: c.declareFunc(n.Function, llvm.VoidType()) case *parser.TemplateNode: c.declareTemplate(n) } } }
func (c *compiler) createInitMainFunction(mainPkg *ssa.Package, initmap map[*types.Package]gccgoimporter.InitData) error { initdata := c.buildPackageInitData(mainPkg, initmap) ftyp := llvm.FunctionType(llvm.VoidType(), nil, false) initMain := llvm.AddFunction(c.module.Module, "__go_init_main", ftyp) c.addCommonFunctionAttrs(initMain) entry := llvm.AddBasicBlock(initMain, "entry") builder := llvm.GlobalContext().NewBuilder() defer builder.Dispose() builder.SetInsertPointAtEnd(entry) for _, init := range initdata.Inits { initfn := c.module.Module.NamedFunction(init.InitFunc) if initfn.IsNil() { initfn = llvm.AddFunction(c.module.Module, init.InitFunc, ftyp) } builder.CreateCall(initfn, nil, "") } builder.CreateRetVoid() return nil }
func (tm *llvmTypeMap) getFunctionTypeInfo(args []types.Type, results []types.Type) (fi functionTypeInfo) { var returnType llvm.Type var argTypes []llvm.Type if len(results) == 0 { returnType = llvm.VoidType() fi.retInf = &directRetInfo{} } else { aik := tm.classify(results...) var resultsType llvm.Type if len(results) == 1 { resultsType = tm.ToLLVM(results[0]) } else { elements := make([]llvm.Type, len(results)) for i := range elements { elements[i] = tm.ToLLVM(results[i]) } resultsType = tm.ctx.StructType(elements, false) } switch aik { case AIK_Direct: var retFields []backendType for _, t := range results { retFields = append(retFields, tm.getBackendType(t)) } bt := &structBType{retFields} retTypes, retAttrs, _, _ := tm.expandType(nil, nil, bt) switch len(retTypes) { case 0: // e.g., empty struct returnType = llvm.VoidType() case 1: returnType = retTypes[0] fi.retAttr = retAttrs[0] case 2: returnType = llvm.StructType(retTypes, false) default: panic("unexpected expandType result") } fi.retInf = &directRetInfo{numResults: len(results), retTypes: retTypes, resultsType: resultsType} case AIK_Indirect: returnType = llvm.VoidType() argTypes = []llvm.Type{llvm.PointerType(resultsType, 0)} fi.argAttrs = []llvm.Attribute{llvm.StructRetAttribute} fi.retInf = &indirectRetInfo{numResults: len(results), resultsType: resultsType} } } // Keep track of the number of INTEGER/SSE class registers remaining. remainingInt := 6 remainingSSE := 8 for _, arg := range args { aik := tm.classify(arg) isDirect := aik == AIK_Direct if isDirect { bt := tm.getBackendType(arg) directArgTypes, directArgAttrs, numInt, numSSE := tm.expandType(argTypes, fi.argAttrs, bt) // Check if the argument can fit into the remaining registers, or if // it would just occupy one register (which pushes the whole argument // onto the stack anyway). if numInt <= remainingInt && numSSE <= remainingSSE || numInt+numSSE == 1 { remainingInt -= numInt remainingSSE -= numSSE argInfo := &directArgInfo{argOffset: len(argTypes), valType: bt.ToLLVM(tm.ctx)} fi.argInfos = append(fi.argInfos, argInfo) argTypes = directArgTypes fi.argAttrs = directArgAttrs argInfo.argTypes = argTypes[argInfo.argOffset:len(argTypes)] } else { // No remaining registers; pass on the stack. isDirect = false } } if !isDirect { fi.argInfos = append(fi.argInfos, &indirectArgInfo{len(argTypes)}) argTypes = append(argTypes, llvm.PointerType(tm.ToLLVM(arg), 0)) fi.argAttrs = append(fi.argAttrs, llvm.ByValAttribute) } } fi.functionType = llvm.FunctionType(returnType, argTypes, false) return }
func newRuntimeInterface(module llvm.Module, tm *llvmTypeMap) (*runtimeInterface, error) { var ri runtimeInterface Bool := types.Typ[types.Bool] Complex128 := types.Typ[types.Complex128] Float64 := types.Typ[types.Float64] Int32 := types.Typ[types.Int32] Int64 := types.Typ[types.Int64] Int := types.Typ[types.Int] Rune := types.Typ[types.Rune] String := types.Typ[types.String] Uintptr := types.Typ[types.Uintptr] UnsafePointer := types.Typ[types.UnsafePointer] EmptyInterface := types.NewInterface(nil, nil) IntSlice := types.NewSlice(types.Typ[types.Int]) for _, rt := range [...]struct { name string rfi *runtimeFnInfo args, res []types.Type attrs []llvm.Attribute }{ { name: "__go_append", rfi: &ri.append, args: []types.Type{IntSlice, UnsafePointer, Uintptr, Uintptr}, res: []types.Type{IntSlice}, }, { name: "__go_assert_interface", rfi: &ri.assertInterface, args: []types.Type{UnsafePointer, UnsafePointer}, res: []types.Type{UnsafePointer}, }, { name: "__go_can_recover", rfi: &ri.canRecover, args: []types.Type{UnsafePointer}, res: []types.Type{Bool}, }, { name: "__go_chan_cap", rfi: &ri.chanCap, args: []types.Type{UnsafePointer}, res: []types.Type{Int}, }, { name: "__go_chan_len", rfi: &ri.chanLen, args: []types.Type{UnsafePointer}, res: []types.Type{Int}, }, { name: "runtime.chanrecv2", rfi: &ri.chanrecv2, args: []types.Type{UnsafePointer, UnsafePointer, UnsafePointer}, res: []types.Type{Bool}, }, { name: "__go_check_defer", rfi: &ri.checkDefer, args: []types.Type{UnsafePointer}, }, { name: "__go_check_interface_type", rfi: &ri.checkInterfaceType, args: []types.Type{UnsafePointer, UnsafePointer, UnsafePointer}, }, { name: "__go_builtin_close", rfi: &ri.builtinClose, args: []types.Type{UnsafePointer}, }, { name: "__go_convert_interface", rfi: &ri.convertInterface, args: []types.Type{UnsafePointer, UnsafePointer}, res: []types.Type{UnsafePointer}, }, { name: "__go_copy", rfi: &ri.copy, args: []types.Type{UnsafePointer, UnsafePointer, Uintptr}, }, { name: "__go_defer", rfi: &ri.Defer, args: []types.Type{UnsafePointer, UnsafePointer, UnsafePointer}, }, { name: "__go_deferred_recover", rfi: &ri.deferredRecover, res: []types.Type{EmptyInterface}, }, { name: "__go_empty_interface_compare", rfi: &ri.emptyInterfaceCompare, args: []types.Type{EmptyInterface, EmptyInterface}, res: []types.Type{Int}, }, { name: "__go_go", rfi: &ri.Go, args: []types.Type{UnsafePointer, UnsafePointer}, }, { name: "runtime.ifaceE2I2", rfi: &ri.ifaceE2I2, args: []types.Type{UnsafePointer, EmptyInterface}, res: []types.Type{EmptyInterface, Bool}, }, { name: "runtime.ifaceI2I2", rfi: &ri.ifaceI2I2, args: []types.Type{UnsafePointer, EmptyInterface}, res: []types.Type{EmptyInterface, Bool}, }, { name: "__go_int_array_to_string", rfi: &ri.intArrayToString, args: []types.Type{UnsafePointer, Int}, res: []types.Type{String}, }, { name: "__go_int_to_string", rfi: &ri.intToString, args: []types.Type{Int}, res: []types.Type{String}, }, { name: "__go_interface_compare", rfi: &ri.interfaceCompare, args: []types.Type{EmptyInterface, EmptyInterface}, res: []types.Type{Int}, }, { name: "__go_make_slice2", rfi: &ri.makeSlice, args: []types.Type{UnsafePointer, Uintptr, Uintptr}, res: []types.Type{IntSlice}, }, { name: "runtime.mapdelete", rfi: &ri.mapdelete, args: []types.Type{UnsafePointer, UnsafePointer}, }, { name: "runtime.mapiter2", rfi: &ri.mapiter2, args: []types.Type{UnsafePointer, UnsafePointer, UnsafePointer}, }, { name: "runtime.mapiterinit", rfi: &ri.mapiterinit, args: []types.Type{UnsafePointer, UnsafePointer}, }, { name: "runtime.mapiternext", rfi: &ri.mapiternext, args: []types.Type{UnsafePointer}, }, { name: "__go_map_index", rfi: &ri.mapIndex, args: []types.Type{UnsafePointer, UnsafePointer, Bool}, res: []types.Type{UnsafePointer}, }, { name: "__go_map_len", rfi: &ri.mapLen, args: []types.Type{UnsafePointer}, res: []types.Type{Int}, }, { name: "__go_new", rfi: &ri.New, args: []types.Type{UnsafePointer, Uintptr}, res: []types.Type{UnsafePointer}, }, { name: "__go_new_channel", rfi: &ri.newChannel, args: []types.Type{UnsafePointer, Uintptr}, res: []types.Type{UnsafePointer}, }, { name: "__go_new_map", rfi: &ri.newMap, args: []types.Type{UnsafePointer, Uintptr}, res: []types.Type{UnsafePointer}, }, { name: "__go_new_nopointers", rfi: &ri.NewNopointers, args: []types.Type{UnsafePointer, Uintptr}, res: []types.Type{UnsafePointer}, }, { name: "runtime.newselect", rfi: &ri.newSelect, args: []types.Type{Int32}, res: []types.Type{UnsafePointer}, }, { name: "__go_panic", rfi: &ri.panic, args: []types.Type{EmptyInterface}, attrs: []llvm.Attribute{llvm.NoReturnAttribute}, }, { name: "__go_print_bool", rfi: &ri.printBool, args: []types.Type{Bool}, }, { name: "__go_print_complex", rfi: &ri.printComplex, args: []types.Type{Complex128}, }, { name: "__go_print_double", rfi: &ri.printDouble, args: []types.Type{Float64}, }, { name: "__go_print_empty_interface", rfi: &ri.printEmptyInterface, args: []types.Type{EmptyInterface}, }, { name: "__go_print_interface", rfi: &ri.printInterface, args: []types.Type{EmptyInterface}, }, { name: "__go_print_int64", rfi: &ri.printInt64, args: []types.Type{Int64}, }, { name: "__go_print_nl", rfi: &ri.printNl, }, { name: "__go_print_pointer", rfi: &ri.printPointer, args: []types.Type{UnsafePointer}, }, { name: "__go_print_slice", rfi: &ri.printSlice, args: []types.Type{IntSlice}, }, { name: "__go_print_space", rfi: &ri.printSpace, }, { name: "__go_print_string", rfi: &ri.printString, args: []types.Type{String}, }, { name: "__go_print_uint64", rfi: &ri.printUint64, args: []types.Type{Int64}, }, { name: "__go_receive", rfi: &ri.receive, args: []types.Type{UnsafePointer, UnsafePointer, UnsafePointer}, }, { name: "__go_recover", rfi: &ri.recover, res: []types.Type{EmptyInterface}, }, { name: "__go_register_gc_roots", rfi: &ri.registerGcRoots, args: []types.Type{UnsafePointer}, }, { name: "__go_runtime_error", rfi: &ri.runtimeError, args: []types.Type{Int32}, attrs: []llvm.Attribute{llvm.NoReturnAttribute}, }, { name: "runtime.selectdefault", rfi: &ri.selectdefault, args: []types.Type{UnsafePointer, Int32}, }, { name: "runtime.selectgo", rfi: &ri.selectgo, args: []types.Type{UnsafePointer}, res: []types.Type{Int}, }, { name: "runtime.selectrecv2", rfi: &ri.selectrecv2, args: []types.Type{UnsafePointer, UnsafePointer, UnsafePointer, UnsafePointer, Int32}, }, { name: "runtime.selectsend", rfi: &ri.selectsend, args: []types.Type{UnsafePointer, UnsafePointer, UnsafePointer, Int32}, }, { name: "__go_send_big", rfi: &ri.sendBig, args: []types.Type{UnsafePointer, UnsafePointer, UnsafePointer}, }, { name: "__go_set_defer_retaddr", rfi: &ri.setDeferRetaddr, args: []types.Type{UnsafePointer}, res: []types.Type{Bool}, }, { name: "__go_strcmp", rfi: &ri.strcmp, args: []types.Type{String, String}, res: []types.Type{Int}, }, { name: "__go_string_plus", rfi: &ri.stringPlus, args: []types.Type{String, String}, res: []types.Type{String}, }, { name: "__go_string_slice", rfi: &ri.stringSlice, args: []types.Type{String, Int, Int}, res: []types.Type{String}, }, { name: "__go_string_to_int_array", rfi: &ri.stringToIntArray, args: []types.Type{String}, res: []types.Type{IntSlice}, }, { name: "runtime.stringiter2", rfi: &ri.stringiter2, args: []types.Type{String, Int}, res: []types.Type{Int, Rune}, }, { name: "__go_type_descriptors_equal", rfi: &ri.typeDescriptorsEqual, args: []types.Type{UnsafePointer, UnsafePointer}, res: []types.Type{Bool}, }, { name: "__go_undefer", rfi: &ri.undefer, args: []types.Type{UnsafePointer}, }, } { rt.rfi.init(tm, module, rt.name, rt.args, rt.res) for _, attr := range rt.attrs { rt.rfi.fn.AddFunctionAttr(attr) } } memsetName := "llvm.memset.p0i8.i" + strconv.Itoa(tm.target.IntPtrType().IntTypeWidth()) memsetType := llvm.FunctionType( llvm.VoidType(), []llvm.Type{ llvm.PointerType(llvm.Int8Type(), 0), llvm.Int8Type(), tm.target.IntPtrType(), llvm.Int32Type(), llvm.Int1Type(), }, false, ) ri.memset = llvm.AddFunction(module, memsetName, memsetType) memcpyName := "llvm.memcpy.p0i8.p0i8.i" + strconv.Itoa(tm.target.IntPtrType().IntTypeWidth()) memcpyType := llvm.FunctionType( llvm.VoidType(), []llvm.Type{ llvm.PointerType(llvm.Int8Type(), 0), llvm.PointerType(llvm.Int8Type(), 0), tm.target.IntPtrType(), llvm.Int32Type(), llvm.Int1Type(), }, false, ) ri.memcpy = llvm.AddFunction(module, memcpyName, memcpyType) returnaddressType := llvm.FunctionType( llvm.PointerType(llvm.Int8Type(), 0), []llvm.Type{llvm.Int32Type()}, false, ) ri.returnaddress = llvm.AddFunction(module, "llvm.returnaddress", returnaddressType) gccgoPersonalityType := llvm.FunctionType( llvm.Int32Type(), []llvm.Type{ llvm.Int32Type(), llvm.Int64Type(), llvm.PointerType(llvm.Int8Type(), 0), llvm.PointerType(llvm.Int8Type(), 0), }, false, ) ri.gccgoPersonality = llvm.AddFunction(module, "__gccgo_personality_v0", gccgoPersonalityType) ri.gccgoExceptionType = llvm.StructType( []llvm.Type{ llvm.PointerType(llvm.Int8Type(), 0), llvm.Int32Type(), }, false, ) return &ri, nil }
// createThunk creates a thunk from a // given function and arguments, suitable for use with // "defer" and "go". func (fr *frame) createThunk(call ssa.CallInstruction) (thunk llvm.Value, arg llvm.Value) { seenarg := make(map[ssa.Value]bool) var args []ssa.Value var argtypes []*types.Var packArg := func(arg ssa.Value) { switch arg.(type) { case *ssa.Builtin, *ssa.Function, *ssa.Const, *ssa.Global: // Do nothing: we can generate these in the thunk default: if !seenarg[arg] { seenarg[arg] = true args = append(args, arg) field := types.NewField(0, nil, "_", arg.Type(), true) argtypes = append(argtypes, field) } } } packArg(call.Common().Value) for _, arg := range call.Common().Args { packArg(arg) } var isRecoverCall bool i8ptr := llvm.PointerType(llvm.Int8Type(), 0) var structllptr llvm.Type if len(args) == 0 { if builtin, ok := call.Common().Value.(*ssa.Builtin); ok { isRecoverCall = builtin.Name() == "recover" } if isRecoverCall { // When creating a thunk for recover(), we must pass fr.canRecover. arg = fr.builder.CreateZExt(fr.canRecover, fr.target.IntPtrType(), "") arg = fr.builder.CreateIntToPtr(arg, i8ptr, "") } else { arg = llvm.ConstPointerNull(i8ptr) } } else { structtype := types.NewStruct(argtypes, nil) arg = fr.createTypeMalloc(structtype) structllptr = arg.Type() for i, ssaarg := range args { argptr := fr.builder.CreateStructGEP(arg, i, "") fr.builder.CreateStore(fr.llvmvalue(ssaarg), argptr) } arg = fr.builder.CreateBitCast(arg, i8ptr, "") } thunkfntype := llvm.FunctionType(llvm.VoidType(), []llvm.Type{i8ptr}, false) thunkfn := llvm.AddFunction(fr.module.Module, "", thunkfntype) thunkfn.SetLinkage(llvm.InternalLinkage) fr.addCommonFunctionAttrs(thunkfn) thunkfr := newFrame(fr.unit, thunkfn) defer thunkfr.dispose() prologuebb := llvm.AddBasicBlock(thunkfn, "prologue") thunkfr.builder.SetInsertPointAtEnd(prologuebb) if isRecoverCall { thunkarg := thunkfn.Param(0) thunkarg = thunkfr.builder.CreatePtrToInt(thunkarg, fr.target.IntPtrType(), "") thunkfr.canRecover = thunkfr.builder.CreateTrunc(thunkarg, llvm.Int1Type(), "") } else if len(args) > 0 { thunkarg := thunkfn.Param(0) thunkarg = thunkfr.builder.CreateBitCast(thunkarg, structllptr, "") for i, ssaarg := range args { thunkargptr := thunkfr.builder.CreateStructGEP(thunkarg, i, "") thunkarg := thunkfr.builder.CreateLoad(thunkargptr, "") thunkfr.env[ssaarg] = newValue(thunkarg, ssaarg.Type()) } } _, isDefer := call.(*ssa.Defer) entrybb := llvm.AddBasicBlock(thunkfn, "entry") br := thunkfr.builder.CreateBr(entrybb) thunkfr.allocaBuilder.SetInsertPointBefore(br) thunkfr.builder.SetInsertPointAtEnd(entrybb) var exitbb llvm.BasicBlock if isDefer { exitbb = llvm.AddBasicBlock(thunkfn, "exit") thunkfr.runtime.setDeferRetaddr.call(thunkfr, llvm.BlockAddress(thunkfn, exitbb)) } if isDefer && isRecoverCall { thunkfr.callRecover(true) } else { thunkfr.callInstruction(call) } if isDefer { thunkfr.builder.CreateBr(exitbb) thunkfr.builder.SetInsertPointAtEnd(exitbb) } thunkfr.builder.CreateRetVoid() thunk = fr.builder.CreateBitCast(thunkfn, i8ptr, "") return }
func (v *Codegen) declareFunctionDecl(n *parser.FunctionDecl) { mangledName := n.Function.MangledName(parser.MANGLE_ARK_UNSTABLE) function := v.curFile.Module.NamedFunction(mangledName) if !function.IsNil() { v.err("function `%s` already exists in module", n.Function.Name) } else { numOfParams := len(n.Function.Parameters) params := make([]llvm.Type, numOfParams) for i, par := range n.Function.Parameters { params[i] = v.typeToLLVMType(par.Variable.Type) } // attributes defaults cBinding := false // find them attributes yo if n.Function.Attrs != nil { attributes := n.Function.Attrs // todo hashmap or some shit for _, attr := range attributes { switch attr.Key { case "c": cBinding = true default: // do nothing } } } // assume it's void funcTypeRaw := llvm.VoidType() // oo theres a type, let's try figure it out if n.Function.ReturnType != nil { funcTypeRaw = v.typeToLLVMType(n.Function.ReturnType) } // create the function type funcType := llvm.FunctionType(funcTypeRaw, params, n.Function.IsVariadic) functionName := mangledName if cBinding { functionName = n.Function.Name } // add that shit function = llvm.AddFunction(v.curFile.Module, functionName, funcType) // do some magical shit for later for i := 0; i < numOfParams; i++ { funcParam := function.Param(i) funcParam.SetName(n.Function.Parameters[i].Variable.MangledName(parser.MANGLE_ARK_UNSTABLE)) } if cBinding { function.SetFunctionCallConv(llvm.CCallConv) } else { function.SetFunctionCallConv(llvm.FastCallConv) } } }
func (v *Codegen) genVariableDecl(n *parser.VariableDecl, semicolon bool) llvm.Value { var res llvm.Value if v.inFunction { mangledName := n.Variable.MangledName(parser.MANGLE_ARK_UNSTABLE) funcEntry := v.currentFunction.EntryBasicBlock() // use this builder for the variable alloca // this means all allocas go at the start of the function // so each variable is only allocated once allocBuilder := llvm.NewBuilder() if funcEntry == v.builder.GetInsertBlock() { allocBuilder.SetInsertPointAtEnd(funcEntry) } else { allocBuilder.SetInsertPointBefore(funcEntry.LastInstruction()) } alloc := allocBuilder.CreateAlloca(v.typeToLLVMType(n.Variable.Type), mangledName) // set allocated memory to zero fn := v.curFile.Module.NamedFunction("llvm.memset.p0i8.i32") if fn.IsNil() { fnType := llvm.FunctionType(llvm.VoidType(), []llvm.Type{llvm.PointerType(llvm.IntType(8), 0), llvm.IntType(8), llvm.IntType(32), llvm.IntType(32), llvm.IntType(1)}, false) fn = llvm.AddFunction(v.curFile.Module, "llvm.memset.p0i8.i32", fnType) } // cast alloc to byte array castAlloc := allocBuilder.CreateBitCast(alloc, llvm.PointerType(llvm.IntType(8), 0), "") // get type length gep := allocBuilder.CreateGEP(llvm.ConstNull(llvm.PointerType(v.typeToLLVMType(n.Variable.Type), 0)), []llvm.Value{llvm.ConstInt(llvm.IntType(32), 1, false)}, "") length := allocBuilder.CreatePtrToInt(gep, llvm.IntType(32), "") // call memset intrinsic allocBuilder.CreateCall(fn, []llvm.Value{castAlloc, llvm.ConstInt(llvm.IntType(8), 0, false), length, llvm.ConstInt(llvm.IntType(32), 0, false), llvm.ConstInt(llvm.IntType(1), 0, false)}, "") allocBuilder.Dispose() v.variableLookup[n.Variable] = alloc if n.Assignment != nil { if value := v.genExpr(n.Assignment); !value.IsNil() { v.builder.CreateStore(value, alloc) } } } else { mangledName := n.Variable.MangledName(parser.MANGLE_ARK_UNSTABLE) varType := v.typeToLLVMType(n.Variable.Type) value := llvm.AddGlobal(v.curFile.Module, varType, mangledName) value.SetLinkage(llvm.InternalLinkage) value.SetGlobalConstant(!n.Variable.Mutable) if n.Assignment != nil { value.SetInitializer(v.genExpr(n.Assignment)) } v.variableLookup[n.Variable] = value } return res }