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 (fr *frame) value(v ssa.Value) (result *govalue) { switch v := v.(type) { case nil: return nil case *ssa.Function: return fr.resolveFunctionDescriptor(v) case *ssa.Const: return fr.newValueFromConst(v.Value, v.Type()) case *ssa.Global: if g, ok := fr.globals[v]; ok { return newValue(g, v.Type()) } // Create an external global. Globals for this package are defined // on entry to translatePackage, and have initialisers. llelemtyp := fr.llvmtypes.ToLLVM(deref(v.Type())) vname := fr.types.mc.mangleGlobalName(v) llglobal := llvm.AddGlobal(fr.module.Module, llelemtyp, vname) llglobal = llvm.ConstBitCast(llglobal, fr.llvmtypes.ToLLVM(v.Type())) fr.globals[v] = llglobal return newValue(llglobal, v.Type()) } if value, ok := fr.env[v]; ok { return value } panic("Instruction not visited yet") }
// 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 *Codegen) generateVarDecl(node *parser.VarDeclNode, global bool) { t := c.getLLVMType(node.Type) name := node.Name.Value if c.scope.Declared(name) { // Error name has already been declared } var alloc, val llvm.Value if node.Value == nil { if t.TypeKind() == llvm.PointerTypeKind { val = c.convert(c.scope.GetValue("null"), t) } else { val = llvm.Undef(t) } } else { val = c.convert(c.generateExpression(node.Value), t) } if !global { alloc = c.builder.CreateAlloca(t, name) c.builder.CreateStore(val, alloc) } else { alloc = llvm.AddGlobal(c.module, t, name) alloc.SetInitializer(val) } c.scope.AddVariable(name, alloc) }
// resolveFunctionDescriptorGlobal returns a reference to the LLVM global // storing the function's descriptor. func (u *unit) resolveFunctionDescriptorGlobal(f *ssa.Function) llvm.Value { llfd, ok := u.funcDescriptors[f] if !ok { name := u.types.mc.mangleFunctionName(f) + "$descriptor" llfd = llvm.AddGlobal(u.module.Module, llvm.PointerType(llvm.Int8Type(), 0), name) llfd.SetGlobalConstant(true) u.funcDescriptors[f] = llfd } return llfd }
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.currentLLVMFunction().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()) } varType := v.typeToLLVMType(n.Variable.Type) alloc := allocBuilder.CreateAlloca(varType, mangledName) 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 { // TODO cbindings cBinding := false mangledName := n.Variable.MangledName(parser.MANGLE_ARK_UNSTABLE) varType := v.typeToLLVMType(n.Variable.Type) value := llvm.AddGlobal(v.curFile.LlvmModule, varType, mangledName) // TODO: External by default to export everything, change once we get access specifiers if !cBinding && !n.IsPublic() { value.SetLinkage(nonPublicLinkage) } value.SetGlobalConstant(!n.Variable.Mutable) if n.Assignment != nil { value.SetInitializer(v.genExpr(n.Assignment)) } v.variableLookup[n.Variable] = value } return res }
func (fr *frame) registerGcRoots() { if len(fr.gcRoots) != 0 { rootty := fr.gcRoots[0].Type() roots := append(fr.gcRoots, llvm.ConstNull(rootty)) rootsarr := llvm.ConstArray(rootty, roots) rootsstruct := llvm.ConstStruct([]llvm.Value{llvm.ConstNull(llvm.PointerType(llvm.Int8Type(), 0)), rootsarr}, false) rootsglobal := llvm.AddGlobal(fr.module.Module, rootsstruct.Type(), "") rootsglobal.SetInitializer(rootsstruct) rootsglobal.SetLinkage(llvm.InternalLinkage) fr.runtime.registerGcRoots.callOnly(fr, llvm.ConstBitCast(rootsglobal, llvm.PointerType(llvm.Int8Type(), 0))) } }
// Allocates a literal array on the stack func (v *Codegen) genArrayLiteral(n *parser.ArrayLiteral) llvm.Value { arrayLLVMType := v.typeToLLVMType(n.Type) memberLLVMType := v.typeToLLVMType(n.Type.(parser.ArrayType).MemberType) if v.inFunction { // allocate backing array arrAlloca := v.builder.CreateAlloca(llvm.ArrayType(memberLLVMType, len(n.Members)), "") // copy the constant array to the backing array for idx, value := range n.Members { gep := v.builder.CreateGEP(arrAlloca, []llvm.Value{llvm.ConstInt(llvm.IntType(32), 0, false), llvm.ConstInt(llvm.IntType(32), uint64(idx), false)}, "") value := v.genExpr(value) v.builder.CreateStore(value, gep) } // allocate struct structAlloca := v.builder.CreateAlloca(arrayLLVMType, "") // set the length of the array lenGEP := v.builder.CreateGEP(structAlloca, []llvm.Value{llvm.ConstInt(llvm.IntType(32), 0, false), llvm.ConstInt(llvm.IntType(32), 0, false)}, "") v.builder.CreateStore(llvm.ConstInt(llvm.IntType(32), uint64(len(n.Members)), false), lenGEP) // set the array pointer to the backing array we allocated arrGEP := v.builder.CreateGEP(structAlloca, []llvm.Value{llvm.ConstInt(llvm.IntType(32), 0, false), llvm.ConstInt(llvm.IntType(32), 1, false)}, "") v.builder.CreateStore(v.builder.CreateBitCast(arrAlloca, llvm.PointerType(llvm.ArrayType(memberLLVMType, 0), 0), ""), arrGEP) return v.builder.CreateLoad(structAlloca, "") } else { backName := fmt.Sprintf("_globarr_back_%d", v.arrayIndex) v.arrayIndex++ backGlob := llvm.AddGlobal(v.curFile.Module, llvm.ArrayType(memberLLVMType, len(n.Members)), backName) backGlob.SetLinkage(llvm.InternalLinkage) backGlob.SetGlobalConstant(false) arrConstVals := make([]llvm.Value, len(n.Members)) for idx, mem := range n.Members { value := v.genExpr(mem) if !value.IsConstant() { v.err("Encountered non-constant value in global array") } arrConstVals[idx] = v.genExpr(mem) } backGlob.SetInitializer(llvm.ConstArray(memberLLVMType, arrConstVals)) lengthVal := llvm.ConstInt(llvm.IntType(32), uint64(len(n.Members)), false) backRef := llvm.ConstBitCast(backGlob, llvm.PointerType(llvm.ArrayType(memberLLVMType, 0), 0)) return llvm.ConstStruct([]llvm.Value{lengthVal, backRef}, false) } }
func (v *Codegen) getVariable(vari *parser.Variable) llvm.Value { if value, ok := v.variableLookup[vari]; ok { return value } if vari.ParentModule != v.curFile.Module { value := llvm.AddGlobal(v.curFile.LlvmModule, v.typeToLLVMType(vari.Type), vari.MangledName(parser.MANGLE_ARK_UNSTABLE)) value.SetLinkage(llvm.ExternalLinkage) v.variableLookup[vari] = value return value } v.err("Encountered undeclared variable `%s` in same modules", vari.Name) return llvm.Value{} }
// translatePackage translates an *ssa.Package into an LLVM module, and returns // the translation unit information. func (u *unit) translatePackage(pkg *ssa.Package) { ms := make([]ssa.Member, len(pkg.Members)) i := 0 for _, m := range pkg.Members { ms[i] = m i++ } sort.Sort(byMemberName(ms)) // Initialize global storage and type descriptors for this package. // We must create globals regardless of whether they're referenced, // hence the duplication in frame.value. for _, m := range ms { switch v := m.(type) { case *ssa.Global: elemtyp := deref(v.Type()) llelemtyp := u.llvmtypes.ToLLVM(elemtyp) vname := u.types.mc.mangleGlobalName(v) global := llvm.AddGlobal(u.module.Module, llelemtyp, vname) if !v.Object().Exported() { global.SetLinkage(llvm.InternalLinkage) } u.addGlobal(global, elemtyp) global = llvm.ConstBitCast(global, u.llvmtypes.ToLLVM(v.Type())) u.globals[v] = global case *ssa.Type: u.types.getTypeDescriptorPointer(v.Type()) } } // Define functions. u.defineFunctionsInOrder(ssautil.AllFunctions(pkg.Prog)) // Emit initializers for type descriptors, which may trigger // the resolution of additional functions. u.types.emitTypeDescInitializers() // Define remaining functions that were resolved during // runtime type mapping, but not defined. u.defineFunctionsInOrder(u.undefinedFuncs) // Set initializers for globals. for global, init := range u.globalInits { initval := init.build(global.Type().ElementType()) global.SetInitializer(initval) } }
// Allocates a literal array on the stack func (v *Codegen) genArrayLiteral(n *parser.CompositeLiteral) llvm.Value { arrayLLVMType := v.typeToLLVMType(n.Type) memberLLVMType := v.typeToLLVMType(n.Type.ActualType().(parser.ArrayType).MemberType) arrayValues := make([]llvm.Value, len(n.Values)) for idx, mem := range n.Values { value := v.genExpr(mem) if !v.inFunction() && !value.IsConstant() { v.err("Encountered non-constant value in global array") } arrayValues[idx] = value } lengthValue := llvm.ConstInt(v.typeToLLVMType(parser.PRIMITIVE_uint), uint64(len(n.Values)), false) var backingArrayPointer llvm.Value if v.inFunction() { // allocate backing array backingArray := v.builder().CreateAlloca(llvm.ArrayType(memberLLVMType, len(n.Values)), "") // copy the constant array to the backing array for idx, value := range arrayValues { gep := v.builder().CreateStructGEP(backingArray, idx, "") v.builder().CreateStore(value, gep) } backingArrayPointer = v.builder().CreateBitCast(backingArray, llvm.PointerType(memberLLVMType, 0), "") } else { backName := fmt.Sprintf("_globarr_back_%d", v.arrayIndex) v.arrayIndex++ backingArray := llvm.AddGlobal(v.curFile.LlvmModule, llvm.ArrayType(memberLLVMType, len(n.Values)), backName) backingArray.SetLinkage(llvm.InternalLinkage) backingArray.SetGlobalConstant(false) backingArray.SetInitializer(llvm.ConstArray(memberLLVMType, arrayValues)) backingArrayPointer = llvm.ConstBitCast(backingArray, llvm.PointerType(memberLLVMType, 0)) } structValue := llvm.Undef(arrayLLVMType) structValue = v.builder().CreateInsertValue(structValue, lengthValue, 0, "") structValue = v.builder().CreateInsertValue(structValue, backingArrayPointer, 1, "") return structValue }
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) 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 }
func (v *Codegen) genStringLiteral(n *parser.StringLiteral) llvm.Value { memberLLVMType := v.typeToLLVMType(parser.PRIMITIVE_u8) nullTerm := n.IsCString length := len(n.Value) if nullTerm { length++ } var backingArrayPointer llvm.Value if v.inFunction() { // allocate backing array backingArray := v.builder().CreateAlloca(llvm.ArrayType(memberLLVMType, length), "stackstr") v.builder().CreateStore(llvm.ConstString(n.Value, nullTerm), backingArray) backingArrayPointer = v.builder().CreateBitCast(backingArray, llvm.PointerType(memberLLVMType, 0), "") } else { backName := fmt.Sprintf("_globarr_back_%d", v.arrayIndex) v.arrayIndex++ backingArray := llvm.AddGlobal(v.curFile.LlvmModule, llvm.ArrayType(memberLLVMType, length), backName) backingArray.SetLinkage(llvm.InternalLinkage) backingArray.SetGlobalConstant(false) backingArray.SetInitializer(llvm.ConstString(n.Value, nullTerm)) backingArrayPointer = llvm.ConstBitCast(backingArray, llvm.PointerType(memberLLVMType, 0)) } if n.Type.ActualType().Equals(parser.ArrayOf(parser.PRIMITIVE_u8)) { lengthValue := llvm.ConstInt(v.typeToLLVMType(parser.PRIMITIVE_uint), uint64(length), false) structValue := llvm.Undef(v.typeToLLVMType(n.Type)) structValue = v.builder().CreateInsertValue(structValue, lengthValue, 0, "") structValue = v.builder().CreateInsertValue(structValue, backingArrayPointer, 1, "") return structValue } else { return backingArrayPointer } }
func (fr *frame) instruction(instr ssa.Instruction) { fr.logf("[%T] %v @ %s\n", instr, instr, fr.pkg.Prog.Fset.Position(instr.Pos())) if fr.GenerateDebug { fr.debug.SetLocation(fr.builder, instr.Pos()) } switch instr := instr.(type) { case *ssa.Alloc: typ := deref(instr.Type()) llvmtyp := fr.llvmtypes.ToLLVM(typ) var value llvm.Value if !instr.Heap { value = fr.env[instr].value fr.memsetZero(value, llvm.SizeOf(llvmtyp)) } else if fr.isInit && fr.shouldStaticallyAllocate(instr) { // If this is the init function and we think it may be beneficial, // allocate memory statically in the object file rather than on the // heap. This allows us to optimize constant stores into such // variables as static initializations. global := llvm.AddGlobal(fr.module.Module, llvmtyp, "") global.SetLinkage(llvm.InternalLinkage) fr.addGlobal(global, typ) ptr := llvm.ConstBitCast(global, llvm.PointerType(llvm.Int8Type(), 0)) fr.env[instr] = newValue(ptr, instr.Type()) } else { value = fr.createTypeMalloc(typ) value.SetName(instr.Comment) value = fr.builder.CreateBitCast(value, llvm.PointerType(llvm.Int8Type(), 0), "") fr.env[instr] = newValue(value, instr.Type()) } case *ssa.BinOp: lhs, rhs := fr.value(instr.X), fr.value(instr.Y) fr.env[instr] = fr.binaryOp(lhs, instr.Op, rhs) case *ssa.Call: tuple := fr.callInstruction(instr) if len(tuple) == 1 { fr.env[instr] = tuple[0] } else { fr.tuples[instr] = tuple } case *ssa.ChangeInterface: x := fr.value(instr.X) // The source type must be a non-empty interface, // as ChangeInterface cannot fail (E2I may fail). if instr.Type().Underlying().(*types.Interface).NumMethods() > 0 { x = fr.changeInterface(x, instr.Type(), false) } else { x = fr.convertI2E(x) } fr.env[instr] = x case *ssa.ChangeType: value := fr.llvmvalue(instr.X) if _, ok := instr.Type().Underlying().(*types.Pointer); ok { value = fr.builder.CreateBitCast(value, fr.llvmtypes.ToLLVM(instr.Type()), "") } fr.env[instr] = newValue(value, instr.Type()) case *ssa.Convert: v := fr.value(instr.X) fr.env[instr] = fr.convert(v, instr.Type()) case *ssa.Defer: fn, arg := fr.createThunk(instr) fr.runtime.Defer.call(fr, fr.frameptr, fn, arg) case *ssa.Extract: var elem llvm.Value if t, ok := fr.tuples[instr.Tuple]; ok { elem = t[instr.Index].value } else { tuple := fr.llvmvalue(instr.Tuple) elem = fr.builder.CreateExtractValue(tuple, instr.Index, instr.Name()) } elemtyp := instr.Type() fr.env[instr] = newValue(elem, elemtyp) case *ssa.Field: fieldtyp := instr.Type() if p, ok := fr.ptr[instr.X]; ok { field := fr.builder.CreateStructGEP(p, instr.Field, instr.Name()) if fr.canAvoidElementLoad(*instr.Referrers()) { fr.ptr[instr] = field } else { fr.env[instr] = newValue(fr.builder.CreateLoad(field, ""), fieldtyp) } } else { value := fr.llvmvalue(instr.X) field := fr.builder.CreateExtractValue(value, instr.Field, instr.Name()) fr.env[instr] = newValue(field, fieldtyp) } case *ssa.FieldAddr: ptr := fr.llvmvalue(instr.X) fr.nilCheck(instr.X, ptr) xtyp := instr.X.Type().Underlying().(*types.Pointer).Elem() ptrtyp := llvm.PointerType(fr.llvmtypes.ToLLVM(xtyp), 0) ptr = fr.builder.CreateBitCast(ptr, ptrtyp, "") fieldptr := fr.builder.CreateStructGEP(ptr, instr.Field, instr.Name()) fieldptr = fr.builder.CreateBitCast(fieldptr, llvm.PointerType(llvm.Int8Type(), 0), "") fieldptrtyp := instr.Type() fr.env[instr] = newValue(fieldptr, fieldptrtyp) case *ssa.Go: fn, arg := fr.createThunk(instr) fr.runtime.Go.call(fr, fn, arg) case *ssa.If: cond := fr.llvmvalue(instr.Cond) block := instr.Block() trueBlock := fr.block(block.Succs[0]) falseBlock := fr.block(block.Succs[1]) cond = fr.builder.CreateTrunc(cond, llvm.Int1Type(), "") fr.builder.CreateCondBr(cond, trueBlock, falseBlock) case *ssa.Index: var arrayptr llvm.Value if ptr, ok := fr.ptr[instr.X]; ok { arrayptr = ptr } else { array := fr.llvmvalue(instr.X) arrayptr = fr.allocaBuilder.CreateAlloca(array.Type(), "") fr.builder.CreateStore(array, arrayptr) } index := fr.llvmvalue(instr.Index) arraytyp := instr.X.Type().Underlying().(*types.Array) arraylen := llvm.ConstInt(fr.llvmtypes.inttype, uint64(arraytyp.Len()), false) // The index may not have been promoted to int (for example, if it // came from a composite literal). index = fr.createZExtOrTrunc(index, fr.types.inttype, "") // Bounds checking: 0 <= index < len zero := llvm.ConstNull(fr.types.inttype) i0 := fr.builder.CreateICmp(llvm.IntSLT, index, zero, "") li := fr.builder.CreateICmp(llvm.IntSLE, arraylen, index, "") cond := fr.builder.CreateOr(i0, li, "") fr.condBrRuntimeError(cond, gccgoRuntimeErrorARRAY_INDEX_OUT_OF_BOUNDS) addr := fr.builder.CreateGEP(arrayptr, []llvm.Value{zero, index}, "") if fr.canAvoidElementLoad(*instr.Referrers()) { fr.ptr[instr] = addr } else { fr.env[instr] = newValue(fr.builder.CreateLoad(addr, ""), instr.Type()) } case *ssa.IndexAddr: x := fr.llvmvalue(instr.X) index := fr.llvmvalue(instr.Index) var arrayptr, arraylen llvm.Value var elemtyp types.Type var errcode uint64 switch typ := instr.X.Type().Underlying().(type) { case *types.Slice: elemtyp = typ.Elem() arrayptr = fr.builder.CreateExtractValue(x, 0, "") arraylen = fr.builder.CreateExtractValue(x, 1, "") errcode = gccgoRuntimeErrorSLICE_INDEX_OUT_OF_BOUNDS case *types.Pointer: // *array arraytyp := typ.Elem().Underlying().(*types.Array) elemtyp = arraytyp.Elem() fr.nilCheck(instr.X, x) arrayptr = x arraylen = llvm.ConstInt(fr.llvmtypes.inttype, uint64(arraytyp.Len()), false) errcode = gccgoRuntimeErrorARRAY_INDEX_OUT_OF_BOUNDS } // The index may not have been promoted to int (for example, if it // came from a composite literal). index = fr.createZExtOrTrunc(index, fr.types.inttype, "") // Bounds checking: 0 <= index < len zero := llvm.ConstNull(fr.types.inttype) i0 := fr.builder.CreateICmp(llvm.IntSLT, index, zero, "") li := fr.builder.CreateICmp(llvm.IntSLE, arraylen, index, "") cond := fr.builder.CreateOr(i0, li, "") fr.condBrRuntimeError(cond, errcode) ptrtyp := llvm.PointerType(fr.llvmtypes.ToLLVM(elemtyp), 0) arrayptr = fr.builder.CreateBitCast(arrayptr, ptrtyp, "") addr := fr.builder.CreateGEP(arrayptr, []llvm.Value{index}, "") addr = fr.builder.CreateBitCast(addr, llvm.PointerType(llvm.Int8Type(), 0), "") fr.env[instr] = newValue(addr, types.NewPointer(elemtyp)) case *ssa.Jump: succ := instr.Block().Succs[0] fr.builder.CreateBr(fr.block(succ)) case *ssa.Lookup: x := fr.value(instr.X) index := fr.value(instr.Index) if isString(x.Type().Underlying()) { fr.env[instr] = fr.stringIndex(x, index) } else { v, ok := fr.mapLookup(x, index) if instr.CommaOk { fr.tuples[instr] = []*govalue{v, ok} } else { fr.env[instr] = v } } case *ssa.MakeChan: fr.env[instr] = fr.makeChan(instr.Type(), fr.value(instr.Size)) case *ssa.MakeClosure: llfn := fr.resolveFunctionGlobal(instr.Fn.(*ssa.Function)) llfn = llvm.ConstBitCast(llfn, llvm.PointerType(llvm.Int8Type(), 0)) fn := newValue(llfn, instr.Fn.(*ssa.Function).Signature) bindings := make([]*govalue, len(instr.Bindings)) for i, binding := range instr.Bindings { bindings[i] = fr.value(binding) } fr.env[instr] = fr.makeClosure(fn, bindings) case *ssa.MakeInterface: // fr.ptr[instr.X] will be set if a pointer load was elided by canAvoidLoad if ptr, ok := fr.ptr[instr.X]; ok { fr.env[instr] = fr.makeInterfaceFromPointer(ptr, instr.X.Type(), instr.Type()) } else { receiver := fr.llvmvalue(instr.X) fr.env[instr] = fr.makeInterface(receiver, instr.X.Type(), instr.Type()) } case *ssa.MakeMap: fr.env[instr] = fr.makeMap(instr.Type(), fr.value(instr.Reserve)) case *ssa.MakeSlice: length := fr.value(instr.Len) capacity := fr.value(instr.Cap) fr.env[instr] = fr.makeSlice(instr.Type(), length, capacity) case *ssa.MapUpdate: m := fr.value(instr.Map) k := fr.value(instr.Key) v := fr.value(instr.Value) fr.mapUpdate(m, k, v) case *ssa.Next: iter := fr.tuples[instr.Iter] if instr.IsString { fr.tuples[instr] = fr.stringIterNext(iter) } else { fr.tuples[instr] = fr.mapIterNext(iter) } case *ssa.Panic: arg := fr.value(instr.X) fr.callPanic(arg) case *ssa.Phi: typ := instr.Type() phi := fr.builder.CreatePHI(fr.llvmtypes.ToLLVM(typ), instr.Comment) fr.env[instr] = newValue(phi, typ) fr.phis = append(fr.phis, pendingPhi{instr, phi}) case *ssa.Range: x := fr.value(instr.X) switch x.Type().Underlying().(type) { case *types.Map: fr.tuples[instr] = fr.mapIterInit(x) case *types.Basic: // string fr.tuples[instr] = fr.stringIterInit(x) default: panic(fmt.Sprintf("unhandled range for type %T", x.Type())) } case *ssa.Return: vals := make([]llvm.Value, len(instr.Results)) for i, res := range instr.Results { vals[i] = fr.llvmvalue(res) } fr.retInf.encode(llvm.GlobalContext(), fr.allocaBuilder, fr.builder, vals) case *ssa.RunDefers: fr.runDefers() case *ssa.Select: states := make([]selectState, len(instr.States)) for i, state := range instr.States { states[i] = selectState{ Dir: state.Dir, Chan: fr.value(state.Chan), Send: fr.value(state.Send), } } index, recvOk, recvElems := fr.chanSelect(states, instr.Blocking) tuple := append([]*govalue{index, recvOk}, recvElems...) fr.tuples[instr] = tuple case *ssa.Send: fr.chanSend(fr.value(instr.Chan), fr.value(instr.X)) case *ssa.Slice: x := fr.llvmvalue(instr.X) low := fr.llvmvalue(instr.Low) high := fr.llvmvalue(instr.High) max := fr.llvmvalue(instr.Max) slice := fr.slice(x, instr.X.Type(), low, high, max) fr.env[instr] = newValue(slice, instr.Type()) case *ssa.Store: addr := fr.llvmvalue(instr.Addr) value := fr.llvmvalue(instr.Val) addr = fr.builder.CreateBitCast(addr, llvm.PointerType(value.Type(), 0), "") // If this is the init function, see if we can simulate the effect // of the store in a global's initializer, in which case we can avoid // generating code for it. if !fr.isInit || !fr.maybeStoreInInitializer(value, addr) { fr.nilCheck(instr.Addr, addr) fr.builder.CreateStore(value, addr) } case *ssa.TypeAssert: x := fr.value(instr.X) if instr.CommaOk { v, ok := fr.interfaceTypeCheck(x, instr.AssertedType) fr.tuples[instr] = []*govalue{v, ok} } else { fr.env[instr] = fr.interfaceTypeAssert(x, instr.AssertedType) } case *ssa.UnOp: operand := fr.value(instr.X) switch instr.Op { case token.ARROW: x, ok := fr.chanRecv(operand, instr.CommaOk) if instr.CommaOk { fr.tuples[instr] = []*govalue{x, ok} } else { fr.env[instr] = x } case token.MUL: fr.nilCheck(instr.X, operand.value) if !fr.canAvoidLoad(instr, operand.value) { // The bitcast is necessary to handle recursive pointer loads. llptr := fr.builder.CreateBitCast(operand.value, llvm.PointerType(fr.llvmtypes.ToLLVM(instr.Type()), 0), "") fr.env[instr] = newValue(fr.builder.CreateLoad(llptr, ""), instr.Type()) } default: fr.env[instr] = fr.unaryOp(operand, instr.Op) } default: panic(fmt.Sprintf("unhandled: %v", instr)) } }
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 }
// newValueFromConst converts a constant value to an LLVM value. func (fr *frame) newValueFromConst(v exact.Value, typ types.Type) *govalue { switch { case v == nil: llvmtyp := fr.types.ToLLVM(typ) return newValue(llvm.ConstNull(llvmtyp), typ) case isString(typ): if isUntyped(typ) { typ = types.Typ[types.String] } llvmtyp := fr.types.ToLLVM(typ) strval := exact.StringVal(v) strlen := len(strval) i8ptr := llvm.PointerType(llvm.Int8Type(), 0) var ptr llvm.Value if strlen > 0 { init := llvm.ConstString(strval, false) ptr = llvm.AddGlobal(fr.module.Module, init.Type(), "") ptr.SetInitializer(init) ptr.SetLinkage(llvm.InternalLinkage) ptr = llvm.ConstBitCast(ptr, i8ptr) } else { ptr = llvm.ConstNull(i8ptr) } len_ := llvm.ConstInt(fr.types.inttype, uint64(strlen), false) llvmvalue := llvm.Undef(llvmtyp) llvmvalue = llvm.ConstInsertValue(llvmvalue, ptr, []uint32{0}) llvmvalue = llvm.ConstInsertValue(llvmvalue, len_, []uint32{1}) return newValue(llvmvalue, typ) case isInteger(typ): if isUntyped(typ) { typ = types.Typ[types.Int] } llvmtyp := fr.types.ToLLVM(typ) var llvmvalue llvm.Value if isUnsigned(typ) { v, _ := exact.Uint64Val(v) llvmvalue = llvm.ConstInt(llvmtyp, v, false) } else { v, _ := exact.Int64Val(v) llvmvalue = llvm.ConstInt(llvmtyp, uint64(v), true) } return newValue(llvmvalue, typ) case isBoolean(typ): if isUntyped(typ) { typ = types.Typ[types.Bool] } return newValue(boolLLVMValue(exact.BoolVal(v)), typ) case isFloat(typ): if isUntyped(typ) { typ = types.Typ[types.Float64] } llvmtyp := fr.types.ToLLVM(typ) floatval, _ := exact.Float64Val(v) llvmvalue := llvm.ConstFloat(llvmtyp, floatval) return newValue(llvmvalue, typ) case typ == types.Typ[types.UnsafePointer]: llvmtyp := fr.types.ToLLVM(typ) v, _ := exact.Uint64Val(v) llvmvalue := llvm.ConstInt(fr.types.inttype, v, false) llvmvalue = llvm.ConstIntToPtr(llvmvalue, llvmtyp) return newValue(llvmvalue, typ) case isComplex(typ): if isUntyped(typ) { typ = types.Typ[types.Complex128] } llvmtyp := fr.types.ToLLVM(typ) floattyp := llvmtyp.StructElementTypes()[0] llvmvalue := llvm.ConstNull(llvmtyp) realv := exact.Real(v) imagv := exact.Imag(v) realfloatval, _ := exact.Float64Val(realv) imagfloatval, _ := exact.Float64Val(imagv) llvmre := llvm.ConstFloat(floattyp, realfloatval) llvmim := llvm.ConstFloat(floattyp, imagfloatval) llvmvalue = llvm.ConstInsertValue(llvmvalue, llvmre, []uint32{0}) llvmvalue = llvm.ConstInsertValue(llvmvalue, llvmim, []uint32{1}) return newValue(llvmvalue, typ) } // Special case for string -> [](byte|rune) if u, ok := typ.Underlying().(*types.Slice); ok && isInteger(u.Elem()) { if v.Kind() == exact.String { strval := fr.newValueFromConst(v, types.Typ[types.String]) return fr.convert(strval, typ) } } panic(fmt.Sprintf("unhandled: t=%s(%T), v=%v(%T)", typ, typ, v, v)) }