func directEncode(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder, argTypes []llvm.Type, args []llvm.Value, val llvm.Value) { valType := val.Type() switch len(argTypes) { case 0: // do nothing case 1: if argTypes[0].C == valType.C { args[0] = val return } alloca := allocaBuilder.CreateAlloca(valType, "") bitcast := builder.CreateBitCast(alloca, llvm.PointerType(argTypes[0], 0), "") builder.CreateStore(val, alloca) args[0] = builder.CreateLoad(bitcast, "") case 2: encodeType := llvm.StructType(argTypes, false) alloca := allocaBuilder.CreateAlloca(valType, "") bitcast := builder.CreateBitCast(alloca, llvm.PointerType(encodeType, 0), "") builder.CreateStore(val, alloca) args[0] = builder.CreateLoad(builder.CreateStructGEP(bitcast, 0, ""), "") args[1] = builder.CreateLoad(builder.CreateStructGEP(bitcast, 1, ""), "") default: panic("unexpected argTypes size") } }
func (v *Codegen) structTypeToLLVMType(typ *parser.StructType) llvm.Type { if t, ok := v.structLookup_UseHelperFunction[typ]; ok { return t } return llvm.StructType(v.structTypeToLLVMTypeFields(typ), typ.Attrs().Contains("packed")) }
func (v *Codegen) addStructType(typ *parser.StructType) { if _, ok := v.structLookup_UseHelperFunction[typ]; ok { return } for _, field := range typ.Variables { if struc, ok := field.Variable.Type.(*parser.StructType); ok { v.addStructType(struc) // TODO check recursive loop } } numOfFields := len(typ.Variables) fields := make([]llvm.Type, numOfFields) packed := false for i, member := range typ.Variables { memberType := v.typeToLLVMType(member.Variable.Type) fields[i] = memberType } structure := llvm.StructType(fields, packed) llvm.AddGlobal(v.curFile.Module, structure, typ.MangledName(parser.MANGLE_ARK_UNSTABLE)) v.structLookup_UseHelperFunction[typ] = structure }
func (v *Codegen) tupleTypeToLLVMType(typ *parser.TupleType) llvm.Type { fields := make([]llvm.Type, len(typ.Members)) for idx, mem := range typ.Members { fields[idx] = v.typeToLLVMType(mem) } return llvm.StructType(fields, false) }
func (v *Codegen) enumTypeToLLVMType(typ parser.EnumType) llvm.Type { if typ.Simple { // TODO: Handle other integer size, maybe dynamic depending on max value? (1 / 2) return llvm.IntType(32) } return llvm.StructType(v.enumTypeToLLVMTypeFields(typ), false) }
func (v *Codegen) tupleTypeToLLVMType(typ *parser.TupleType) llvm.Type { // TODO: Maybe move to lookup table like struct var fields []llvm.Type for _, mem := range typ.Members { fields = append(fields, v.typeToLLVMType(mem)) } return llvm.StructType(fields, false) }
func (v *Codegen) enumTypeToLLVMType(typ *parser.EnumType) llvm.Type { if typ.Simple { // TODO: Handle other integer size, maybe dynamic depending on max value? (1 / 2) return llvm.IntType(32) } if t, ok := v.enumLookup_UseHelperFunction[typ]; ok { return t } return llvm.StructType(v.enumTypeToLLVMTypeFields(typ), false) }
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 }
func (v *Codegen) arrayTypeToLLVMType(typ parser.ArrayType) llvm.Type { fields := []llvm.Type{llvm.IntType(32), llvm.PointerType(llvm.ArrayType(v.typeToLLVMType(typ.MemberType), 0), 0)} return llvm.StructType(fields, false) }
func (u *unit) defineFunction(f *ssa.Function) { // Only define functions from this package, or synthetic // wrappers (which do not have a package). if f.Pkg != nil && f.Pkg != u.pkg { return } llfn := u.resolveFunctionGlobal(f) linkage := u.getFunctionLinkage(f) isMethod := f.Signature.Recv() != nil // Methods cannot be referred to via a descriptor. if !isMethod { llfd := u.resolveFunctionDescriptorGlobal(f) llfd.SetInitializer(llvm.ConstBitCast(llfn, llvm.PointerType(llvm.Int8Type(), 0))) llfd.SetLinkage(linkage) } // We only need to emit a descriptor for functions without bodies. if len(f.Blocks) == 0 { return } ssaopt.LowerAllocsToStack(f) if u.DumpSSA { f.WriteTo(os.Stderr) } fr := newFrame(u, llfn) defer fr.dispose() fr.addCommonFunctionAttrs(fr.function) fr.function.SetLinkage(linkage) fr.logf("Define function: %s", f.String()) fti := u.llvmtypes.getSignatureInfo(f.Signature) delete(u.undefinedFuncs, f) fr.retInf = fti.retInf // Push the compile unit and function onto the debug context. if u.GenerateDebug { u.debug.PushFunction(fr.function, f.Signature, f.Pos()) defer u.debug.PopFunction() u.debug.SetLocation(fr.builder, f.Pos()) } // If a function calls recover, we create a separate function to // hold the real function, and this function calls __go_can_recover // and bridges to it. if callsRecover(f) { fr = fr.bridgeRecoverFunc(fr.function, fti) } fr.blocks = make([]llvm.BasicBlock, len(f.Blocks)) fr.lastBlocks = make([]llvm.BasicBlock, len(f.Blocks)) for i, block := range f.Blocks { fr.blocks[i] = llvm.AddBasicBlock(fr.function, fmt.Sprintf(".%d.%s", i, block.Comment)) } fr.builder.SetInsertPointAtEnd(fr.blocks[0]) prologueBlock := llvm.InsertBasicBlock(fr.blocks[0], "prologue") fr.builder.SetInsertPointAtEnd(prologueBlock) // Map parameter positions to indices. We use this // when processing locals to map back to parameters // when generating debug metadata. paramPos := make(map[token.Pos]int) for i, param := range f.Params { paramPos[param.Pos()] = i llparam := fti.argInfos[i].decode(llvm.GlobalContext(), fr.builder, fr.builder) if isMethod && i == 0 { if _, ok := param.Type().Underlying().(*types.Pointer); !ok { llparam = fr.builder.CreateBitCast(llparam, llvm.PointerType(fr.types.ToLLVM(param.Type()), 0), "") llparam = fr.builder.CreateLoad(llparam, "") } } fr.env[param] = newValue(llparam, param.Type()) } // Load closure, extract free vars. if len(f.FreeVars) > 0 { for _, fv := range f.FreeVars { fr.env[fv] = newValue(llvm.ConstNull(u.llvmtypes.ToLLVM(fv.Type())), fv.Type()) } elemTypes := make([]llvm.Type, len(f.FreeVars)+1) elemTypes[0] = llvm.PointerType(llvm.Int8Type(), 0) // function pointer for i, fv := range f.FreeVars { elemTypes[i+1] = u.llvmtypes.ToLLVM(fv.Type()) } structType := llvm.StructType(elemTypes, false) closure := fr.runtime.getClosure.call(fr)[0] closure = fr.builder.CreateBitCast(closure, llvm.PointerType(structType, 0), "") for i, fv := range f.FreeVars { ptr := fr.builder.CreateStructGEP(closure, i+1, "") ptr = fr.builder.CreateLoad(ptr, "") fr.env[fv] = newValue(ptr, fv.Type()) } } // Allocate stack space for locals in the prologue block. for _, local := range f.Locals { typ := fr.llvmtypes.ToLLVM(deref(local.Type())) alloca := fr.builder.CreateAlloca(typ, local.Comment) fr.memsetZero(alloca, llvm.SizeOf(typ)) bcalloca := fr.builder.CreateBitCast(alloca, llvm.PointerType(llvm.Int8Type(), 0), "") value := newValue(bcalloca, local.Type()) fr.env[local] = value if fr.GenerateDebug { paramIndex, ok := paramPos[local.Pos()] if !ok { paramIndex = -1 } fr.debug.Declare(fr.builder, local, alloca, paramIndex) } } // If this is the "init" function, enable init-specific optimizations. if !isMethod && f.Name() == "init" { fr.isInit = true } // If the function contains any defers, we must first create // an unwind block. We can short-circuit the check for defers with // f.Recover != nil. if f.Recover != nil || hasDefer(f) { fr.unwindBlock = llvm.AddBasicBlock(fr.function, "") fr.frameptr = fr.builder.CreateAlloca(llvm.Int8Type(), "") } term := fr.builder.CreateBr(fr.blocks[0]) fr.allocaBuilder.SetInsertPointBefore(term) for _, block := range f.DomPreorder() { fr.translateBlock(block, fr.blocks[block.Index]) } fr.fixupPhis() if !fr.unwindBlock.IsNil() { fr.setupUnwindBlock(f.Recover, f.Signature.Results()) } // The init function needs to register the GC roots first. We do this // after generating code for it because allocations may have caused // additional GC roots to be created. if fr.isInit { fr.builder.SetInsertPointBefore(prologueBlock.FirstInstruction()) fr.registerGcRoots() } }
func (v *Codegen) structTypeToLLVMType(typ parser.StructType) llvm.Type { return llvm.StructType(v.structTypeToLLVMTypeFields(typ), typ.Attrs().Contains("packed")) }
func (v *Codegen) arrayTypeToLLVMType(typ parser.ArrayType) llvm.Type { fields := []llvm.Type{v.typeToLLVMType(parser.PRIMITIVE_uint), llvm.PointerType(v.typeToLLVMType(typ.MemberType), 0)} return llvm.StructType(fields, false) }