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 (c *Codegen) generateArithmeticBinaryExpr(left, right llvm.Value, op string) llvm.Value { t := c.getUnderlyingType(left.Type()) switch op { case "+": if t == PRIMITIVE_TYPES["float"] { return c.builder.CreateFAdd(left, right, "") } else if t == PRIMITIVE_TYPES["int"] { return c.builder.CreateAdd(left, right, "") } else if t == c.templates["string"].Type { return c.generateStringConcat(left, right) } case "-": if t == PRIMITIVE_TYPES["float"] { return c.builder.CreateFSub(left, right, "") } else if t == PRIMITIVE_TYPES["int"] { return c.builder.CreateSub(left, right, "") } case "*": if t == PRIMITIVE_TYPES["float"] { return c.builder.CreateFMul(left, right, "") } else if t == PRIMITIVE_TYPES["int"] { return c.builder.CreateMul(left, right, "") } case "/": if t == PRIMITIVE_TYPES["float"] { return c.builder.CreateFDiv(left, right, "") } else if t == PRIMITIVE_TYPES["int"] { return c.builder.CreateSDiv(left, right, "") } } return null }
// getBBName returns the name (or ID if unnamed) of a basic block. func getBBName(v llvm.Value) (string, error) { if !v.IsBasicBlock() { return "", errutil.Newf("invalid value type; expected basic block, got %v", v.Type()) } // Locate the name of a named basic block. if name := v.Name(); len(name) > 0 { return name, nil } // Locate the ID of an unnamed basic block by parsing the value dump in // search for its basic block label. // // Example value dump: // 0: // br i1 true, label %1, label %2 // // Each basic block is expected to have a label, which requires the // "unnamed.patch" to be applied to the llvm.org/llvm/bindings/go/llvm code // base. s, err := hackDump(v) if err != nil { return "", errutil.Err(err) } tokens := lexer.ParseString(s) if len(tokens) < 1 { return "", errutil.Newf("unable to locate basic block label in %q", s) } tok := tokens[0] if tok.Kind != token.Label { return "", errutil.Newf("invalid token; expected %v, got %v", token.Label, tok.Kind) } return tok.Val, nil }
func (fr *frame) createZExtOrTrunc(v llvm.Value, t llvm.Type, name string) llvm.Value { switch n := v.Type().IntTypeWidth() - t.IntTypeWidth(); { case n < 0: v = fr.builder.CreateZExt(v, fr.target.IntPtrType(), name) case n > 0: v = fr.builder.CreateTrunc(v, fr.target.IntPtrType(), name) } return v }
func (c *Codegen) unbox(val llvm.Value) llvm.Value { t := c.getUnderlyingType(val.Type()) switch t { case c.templates["string"].Type: val = c.builder.CreateStructGEP(val, 0, "") val = c.builder.CreateLoad(val, "") } return val }
func (c *Codegen) convert(val llvm.Value, t llvm.Type) llvm.Value { if val.Type() == t { return val } if val == c.scope.GetValue("null") { log.Println("Null conversion") return llvm.ConstPointerNull(t) } switch val.Type() { case PRIMITIVE_TYPES["int"]: if t == PRIMITIVE_TYPES["float"] { return c.builder.CreateSIToFP(val, t, "") } } return val }
// If val is a constant and addr refers to a global variable which is defined in // this module or an element thereof, simulate the effect of storing val at addr // in the global variable's initializer and return true, otherwise return false. // Precondition: we are compiling the init function. func (fr *frame) maybeStoreInInitializer(val, addr llvm.Value) bool { if val.IsAConstant().IsNil() { return false } if !addr.IsAConstantExpr().IsNil() && addr.OperandsCount() >= 2 && // TODO(pcc): Explicitly check that this is a constant GEP. // I don't think there are any other kinds of constantexpr which // satisfy the conditions we test for here, so this is probably safe. !addr.Operand(0).IsAGlobalVariable().IsNil() && addr.Operand(1).IsNull() { gv := addr.Operand(0) globalInit, ok := fr.globalInits[gv] if !ok { return false } indices := make([]uint32, addr.OperandsCount()-2) for i := range indices { op := addr.Operand(i + 2) if op.IsAConstantInt().IsNil() { return false } indices[i] = uint32(op.ZExtValue()) } globalInit.update(gv.Type().ElementType(), indices, val) return true } else if !addr.IsAGlobalVariable().IsNil() { if globalInit, ok := fr.globalInits[addr]; ok { globalInit.update(addr.Type().ElementType(), nil, val) return true } return false } else { return false } }
func (c *Codegen) generateComparisonBinaryExpr(left, right llvm.Value, op string) llvm.Value { t := c.getUnderlyingType(left.Type()) if t == PRIMITIVE_TYPES["float"] { return c.builder.CreateFCmp(floatPredicates[op], left, right, "") } else if t == PRIMITIVE_TYPES["int"] || op == "==" || op == "!=" { log.Println("Left:", left.Type(), "Right:", right.Type()) return c.builder.CreateICmp(intPredicates[op], left, right, "") } return null }
func (ai *indirectArgInfo) encode(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder, args []llvm.Value, val llvm.Value) { alloca := allocaBuilder.CreateAlloca(val.Type(), "") builder.CreateStore(val, alloca) args[ai.argOffset] = alloca }
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) genBoundsCheck(limit llvm.Value, index llvm.Value, indexType parser.Type) { segvBlock := llvm.AddBasicBlock(v.currentLLVMFunction(), "boundscheck_segv") endBlock := llvm.AddBasicBlock(v.currentLLVMFunction(), "boundscheck_end") upperCheckBlock := llvm.AddBasicBlock(v.currentLLVMFunction(), "boundscheck_upper_block") tooLow := v.builder().CreateICmp(llvm.IntSGT, llvm.ConstInt(index.Type(), 0, false), index, "boundscheck_lower") v.builder().CreateCondBr(tooLow, segvBlock, upperCheckBlock) v.builder().SetInsertPointAtEnd(upperCheckBlock) // make sure limit and index have same width castedLimit := limit castedIndex := index if index.Type().IntTypeWidth() < limit.Type().IntTypeWidth() { if indexType.IsSigned() { castedIndex = v.builder().CreateSExt(index, limit.Type(), "") } else { castedIndex = v.builder().CreateZExt(index, limit.Type(), "") } } else if index.Type().IntTypeWidth() > limit.Type().IntTypeWidth() { castedLimit = v.builder().CreateZExt(limit, index.Type(), "") } tooHigh := v.builder().CreateICmp(llvm.IntSLE, castedLimit, castedIndex, "boundscheck_upper") v.builder().CreateCondBr(tooHigh, segvBlock, endBlock) v.builder().SetInsertPointAtEnd(segvBlock) v.genRaiseSegfault() v.builder().CreateUnreachable() v.builder().SetInsertPointAtEnd(endBlock) }