Beispiel #1
0
// makeSlice allocates a new slice with the optional length and capacity,
// initialising its contents to their zero values.
func (c *compiler) makeSlice(elttyp types.Type, length, capacity Value) llvm.Value {
	var lengthValue llvm.Value
	if length != nil {
		lengthValue = length.Convert(types.Typ[types.Int]).LLVMValue()
	} else {
		lengthValue = llvm.ConstNull(c.llvmtypes.inttype)
	}

	// TODO check capacity >= length
	capacityValue := lengthValue
	if capacity != nil {
		capacityValue = capacity.Convert(types.Typ[types.Int]).LLVMValue()
	}

	eltType := c.types.ToLLVM(elttyp)
	sizeof := llvm.ConstTruncOrBitCast(llvm.SizeOf(eltType), c.types.inttype)
	size := c.builder.CreateMul(capacityValue, sizeof, "")
	mem := c.createMalloc(size)
	mem = c.builder.CreateIntToPtr(mem, llvm.PointerType(eltType, 0), "")
	c.memsetZero(mem, size)

	slicetyp := types.NewSlice(elttyp)
	struct_ := llvm.Undef(c.types.ToLLVM(slicetyp))
	struct_ = c.builder.CreateInsertValue(struct_, mem, 0, "")
	struct_ = c.builder.CreateInsertValue(struct_, lengthValue, 1, "")
	struct_ = c.builder.CreateInsertValue(struct_, capacityValue, 2, "")
	return struct_
}
Beispiel #2
0
// makeSlice allocates a new slice with the optional length and capacity,
// initialising its contents to their zero values.
func (c *compiler) makeSlice(elttyp types.Type, length, capacity Value) llvm.Value {
	var lengthValue llvm.Value
	if length != nil {
		lengthValue = length.Convert(types.Int32).LLVMValue()
	} else {
		lengthValue = llvm.ConstNull(llvm.Int32Type())
	}

	// TODO check capacity >= length
	capacityValue := lengthValue
	if capacity != nil {
		capacityValue = capacity.Convert(types.Int32).LLVMValue()
	}

	llvmelttyp := c.types.ToLLVM(elttyp)
	mem := c.builder.CreateArrayMalloc(llvmelttyp, capacityValue, "")
	sizeof := llvm.ConstTrunc(llvm.SizeOf(llvmelttyp), llvm.Int32Type())
	size := c.builder.CreateMul(capacityValue, sizeof, "")
	c.memsetZero(mem, size)

	slicetyp := types.Slice{Elt: elttyp}
	struct_ := llvm.Undef(c.types.ToLLVM(&slicetyp))
	struct_ = c.builder.CreateInsertValue(struct_, mem, 0, "")
	struct_ = c.builder.CreateInsertValue(struct_, lengthValue, 1, "")
	struct_ = c.builder.CreateInsertValue(struct_, capacityValue, 2, "")
	return struct_
}
Beispiel #3
0
func (c *compiler) VisitLen(expr *ast.CallExpr) Value {
	if len(expr.Args) > 1 {
		panic("Expecting only one argument to len")
	}

	value := c.VisitExpr(expr.Args[0])
	typ := value.Type()
	if name, ok := typ.(*types.Name); ok {
		typ = name.Underlying
	}

	switch typ := typ.(type) {
	case *types.Pointer:
		// XXX Converting to a string to be converted back to an int is
		// silly. The values need an overhaul? Perhaps have types based
		// on fundamental types, with the additional methods to make
		// them llgo.Value's.
		if a, isarray := typ.Base.(*types.Array); isarray {
			return c.NewConstValue(token.INT,
				strconv.FormatUint(a.Len, 10))
		}
		v := strconv.FormatUint(uint64(unsafe.Sizeof(uintptr(0))), 10)
		return c.NewConstValue(token.INT, v)

	case *types.Slice:
		ptr := value.(*LLVMValue).pointer
		len_field := c.builder.CreateStructGEP(ptr.LLVMValue(), 1, "")
		len_value := c.builder.CreateLoad(len_field, "")
		return c.NewLLVMValue(len_value, types.Int32).Convert(types.Int)

	case *types.Array:
		v := strconv.FormatUint(typ.Len, 10)
		return c.NewConstValue(token.INT, v)

	case *types.Struct:
		sz := llvm.SizeOf(c.types.ToLLVM(typ))
		// FIXME
		// SizeOf returns a Constant, but not a ConstantInt, so we
		// can't call ZExtValue on it. Not sure how best to tackle
		// this, so for now returning this as a non-const value.
		//return c.NewConstValue(token.INT, string(sz.ZExtValue()))
		return c.NewLLVMValue(sz, types.Int)

	case *types.Basic:
		if typ == types.String.Underlying {
			ptr := value.(*LLVMValue).pointer
			len_field := c.builder.CreateStructGEP(ptr.LLVMValue(), 1, "")
			len_value := c.builder.CreateLoad(len_field, "")
			return c.NewLLVMValue(len_value, types.Int32).Convert(types.Int)
		}
	}
	panic(fmt.Sprint("Unhandled value type: ", value.Type()))
}
Beispiel #4
0
// makeLiteralSlice allocates a new slice, storing in it the provided elements.
func (c *compiler) makeLiteralSlice(v []llvm.Value, elttyp types.Type) llvm.Value {
	n := llvm.ConstInt(c.types.inttype, uint64(len(v)), false)
	eltType := c.types.ToLLVM(elttyp)
	arrayType := llvm.ArrayType(eltType, len(v))
	mem := c.createMalloc(llvm.SizeOf(arrayType))
	mem = c.builder.CreateIntToPtr(mem, llvm.PointerType(eltType, 0), "")
	for i, value := range v {
		indices := []llvm.Value{llvm.ConstInt(llvm.Int32Type(), uint64(i), false)}
		ep := c.builder.CreateGEP(mem, indices, "")
		c.builder.CreateStore(value, ep)
	}
	slicetyp := types.NewSlice(elttyp)
	struct_ := llvm.Undef(c.types.ToLLVM(slicetyp))
	struct_ = c.builder.CreateInsertValue(struct_, mem, 0, "")
	struct_ = c.builder.CreateInsertValue(struct_, n, 1, "")
	struct_ = c.builder.CreateInsertValue(struct_, n, 2, "")
	return struct_
}
Beispiel #5
0
func (tm *TypeMap) makeRtype(t types.Type, k reflect.Kind) llvm.Value {
	// Not sure if there's an easier way to do this, but if you just
	// use ConstStruct, you end up getting a different llvm.Type.
	lt := tm.ToLLVM(t)
	typ := llvm.ConstNull(tm.runtimeType)
	elementTypes := tm.runtimeType.StructElementTypes()

	// Size.
	size := llvm.SizeOf(lt)
	if size.Type().IntTypeWidth() > elementTypes[0].IntTypeWidth() {
		size = llvm.ConstTrunc(size, elementTypes[0])
	}
	typ = llvm.ConstInsertValue(typ, size, []uint32{0})

	// TODO hash
	// TODO padding

	// Alignment.
	align := llvm.ConstTrunc(llvm.AlignOf(lt), llvm.Int8Type())
	typ = llvm.ConstInsertValue(typ, align, []uint32{3}) // var
	typ = llvm.ConstInsertValue(typ, align, []uint32{4}) // field

	// Kind.
	kind := llvm.ConstInt(llvm.Int8Type(), uint64(k), false)
	typ = llvm.ConstInsertValue(typ, kind, []uint32{5})

	// Algorithm table.
	alg := tm.makeAlgorithmTable(t)
	algptr := llvm.AddGlobal(tm.module, alg.Type(), "")
	algptr.SetInitializer(alg)
	algptr = llvm.ConstBitCast(algptr, elementTypes[6])
	typ = llvm.ConstInsertValue(typ, algptr, []uint32{6})

	// String representation.
	stringrep := tm.globalStringPtr(tm.TypeString(t))
	typ = llvm.ConstInsertValue(typ, stringrep, []uint32{8})

	// TODO gc
	return typ
}
Beispiel #6
0
Datei: ssa.go Projekt: pcc/llgo
func (fr *frame) instruction(instr ssa.Instruction) {
	fr.logf("[%T] %v @ %s\n", instr, instr, fr.pkg.Prog.Fset.Position(instr.Pos()))

	// Check if we'll need to backpatch; see comment
	// in fr.value().
	if v, ok := instr.(ssa.Value); ok {
		if b := fr.backpatcher(v); b != nil {
			defer b()
		}
	}

	switch instr := instr.(type) {
	case *ssa.Alloc:
		typ := fr.llvmtypes.ToLLVM(deref(instr.Type()))
		var value llvm.Value
		if instr.Heap {
			value = fr.createTypeMalloc(typ)
			value.SetName(instr.Comment)
			fr.env[instr] = fr.NewValue(value, instr.Type())
		} else {
			value = fr.env[instr].LLVMValue()
		}
		fr.memsetZero(value, llvm.SizeOf(typ))

	case *ssa.BinOp:
		lhs, rhs := fr.value(instr.X), fr.value(instr.Y)
		fr.env[instr] = lhs.BinaryOp(instr.Op, rhs).(*LLVMValue)

	case *ssa.Call:
		fn, args, result := fr.prepareCall(instr)
		// Some builtins may only be used immediately, and not
		// deferred; in this case, "fn" will be nil, and result
		// may be non-nil (it will be nil for builtins without
		// results.)
		if fn == nil {
			if result != nil {
				fr.env[instr] = result
			}
		} else {
			result = fr.createCall(fn, args)
			fr.env[instr] = result
		}

	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 {
			// TODO(axw) optimisation for I2I case where we
			// know statically the methods to carry over.
			x = x.convertI2E()
			x, _ = x.convertE2I(instr.Type())
		} else {
			x = x.convertI2E()
			x = fr.NewValue(x.LLVMValue(), instr.Type())
		}
		fr.env[instr] = x

	case *ssa.ChangeType:
		value := fr.value(instr.X).LLVMValue()
		if _, ok := instr.Type().Underlying().(*types.Pointer); ok {
			value = fr.builder.CreateBitCast(value, fr.llvmtypes.ToLLVM(instr.Type()), "")
		}
		v := fr.NewValue(value, instr.Type())
		if _, ok := instr.X.(*ssa.Phi); ok {
			v = phiValue(fr.compiler, v)
		}
		fr.env[instr] = v

	case *ssa.Convert:
		v := fr.value(instr.X)
		if _, ok := instr.X.(*ssa.Phi); ok {
			v = phiValue(fr.compiler, v)
		}
		fr.env[instr] = v.Convert(instr.Type()).(*LLVMValue)

	//case *ssa.DebugRef:

	case *ssa.Defer:
		fn, args, result := fr.prepareCall(instr)
		if result != nil {
			panic("illegal use of builtin in defer statement")
		}
		fn = fr.indirectFunction(fn, args)
		fr.createCall(fr.runtime.pushdefer, []*LLVMValue{fn})

	case *ssa.Extract:
		tuple := fr.value(instr.Tuple).LLVMValue()
		elem := fr.builder.CreateExtractValue(tuple, instr.Index, instr.Name())
		elemtyp := instr.Type()
		fr.env[instr] = fr.NewValue(elem, elemtyp)

	case *ssa.Field:
		value := fr.value(instr.X).LLVMValue()
		field := fr.builder.CreateExtractValue(value, instr.Field, instr.Name())
		fieldtyp := instr.Type()
		fr.env[instr] = fr.NewValue(field, fieldtyp)

	case *ssa.FieldAddr:
		// TODO: implement nil check and panic.
		// TODO: combine a chain of {Field,Index}Addrs into a single GEP.
		ptr := fr.value(instr.X).LLVMValue()
		fieldptr := fr.builder.CreateStructGEP(ptr, instr.Field, instr.Name())
		fieldptrtyp := instr.Type()
		fr.env[instr] = fr.NewValue(fieldptr, fieldptrtyp)

	case *ssa.Go:
		fn, args, result := fr.prepareCall(instr)
		if result != nil {
			panic("illegal use of builtin in go statement")
		}
		fn = fr.indirectFunction(fn, args)
		fr.createCall(fr.runtime.Go, []*LLVMValue{fn})

	case *ssa.If:
		cond := fr.value(instr.Cond).LLVMValue()
		block := instr.Block()
		trueBlock := fr.block(block.Succs[0])
		falseBlock := fr.block(block.Succs[1])
		fr.builder.CreateCondBr(cond, trueBlock, falseBlock)

	case *ssa.Index:
		// FIXME Surely we should be dealing with an
		// *array, so we can do a GEP?
		array := fr.value(instr.X).LLVMValue()
		arrayptr := fr.builder.CreateAlloca(array.Type(), "")
		fr.builder.CreateStore(array, arrayptr)
		index := fr.value(instr.Index).LLVMValue()
		zero := llvm.ConstNull(index.Type())
		addr := fr.builder.CreateGEP(arrayptr, []llvm.Value{zero, index}, "")
		fr.env[instr] = fr.NewValue(fr.builder.CreateLoad(addr, ""), instr.Type())

	case *ssa.IndexAddr:
		// TODO: implement nil-check and panic.
		// TODO: combine a chain of {Field,Index}Addrs into a single GEP.
		x := fr.value(instr.X).LLVMValue()
		index := fr.value(instr.Index).LLVMValue()
		var addr llvm.Value
		var elemtyp types.Type
		zero := llvm.ConstNull(index.Type())
		switch typ := instr.X.Type().Underlying().(type) {
		case *types.Slice:
			elemtyp = typ.Elem()
			x = fr.builder.CreateExtractValue(x, 0, "")
			addr = fr.builder.CreateGEP(x, []llvm.Value{index}, "")
		case *types.Pointer: // *array
			elemtyp = typ.Elem().Underlying().(*types.Array).Elem()
			addr = fr.builder.CreateGEP(x, []llvm.Value{zero, index}, "")
		}
		fr.env[instr] = fr.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 {
			fr.env[instr] = fr.mapLookup(x, index, instr.CommaOk)
		}

	case *ssa.MakeChan:
		fr.env[instr] = fr.makeChan(instr.Type(), fr.value(instr.Size))

	case *ssa.MakeClosure:
		fn := fr.resolveFunction(instr.Fn.(*ssa.Function))
		bindings := make([]*LLVMValue, len(instr.Bindings))
		for i, binding := range instr.Bindings {
			bindings[i] = fr.value(binding)
		}
		fr.env[instr] = fr.makeClosure(fn, bindings)

	case *ssa.MakeInterface:
		receiver := fr.value(instr.X)
		fr.env[instr] = fr.makeInterface(receiver, 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.value(instr.Iter)
		if !instr.IsString {
			fr.env[instr] = fr.mapIterNext(iter)
			return
		}

		// String range
		//
		// We make some assumptions for now around the
		// current state of affairs in go.tools/ssa.
		//
		//  - Range's block is a predecessor of Next's.
		//      (this is currently true, but may change in the future;
		//       adonovan says he will expose the dominator tree
		//       computation in the future, which we can use here).
		//  - Next is the first non-Phi instruction in its block.
		//      (this is not strictly necessary; we can move the Phi
		//       to the top of the block, and defer the tuple creation
		//       to Extract).
		assert(instr.Iter.(*ssa.Range).Block() == instr.Block().Preds[0])
		for _, blockInstr := range instr.Block().Instrs {
			if instr == blockInstr {
				break
			}
			_, isphi := blockInstr.(*ssa.Phi)
			assert(isphi)
		}
		preds := instr.Block().Preds
		llpreds := make([]llvm.BasicBlock, len(preds))
		for i, b := range preds {
			llpreds[i] = fr.block(b)
		}
		fr.env[instr] = fr.stringIterNext(iter, llpreds)

	case *ssa.Panic:
		arg := fr.value(instr.X).LLVMValue()
		fr.builder.CreateCall(fr.runtime.panic_.LLVMValue(), []llvm.Value{arg}, "")
		fr.builder.CreateUnreachable()

	case *ssa.Phi:
		typ := instr.Type()
		phi := fr.builder.CreatePHI(fr.llvmtypes.ToLLVM(typ), instr.Comment)
		fr.env[instr] = fr.NewValue(phi, typ)
		values := make([]llvm.Value, len(instr.Edges))
		blocks := make([]llvm.BasicBlock, len(instr.Edges))
		block := instr.Block()
		for i, edge := range instr.Edges {
			values[i] = fr.value(edge).LLVMValue()
			blocks[i] = fr.block(block.Preds[i])
		}
		phi.AddIncoming(values, blocks)

	case *ssa.Range:
		x := fr.value(instr.X)
		switch x.Type().Underlying().(type) {
		case *types.Map:
			fr.env[instr] = fr.mapIterInit(x)
		case *types.Basic: // string
			fr.env[instr] = x
		default:
			panic(fmt.Sprintf("unhandled range for type %T", x.Type()))
		}

	case *ssa.Return:
		switch n := len(instr.Results); n {
		case 0:
			// https://code.google.com/p/go/issues/detail?id=7022
			if r := instr.Parent().Signature.Results(); r != nil && r.Len() > 0 {
				fr.builder.CreateUnreachable()
			} else {
				fr.builder.CreateRetVoid()
			}
		case 1:
			fr.builder.CreateRet(fr.value(instr.Results[0]).LLVMValue())
		default:
			values := make([]llvm.Value, n)
			for i, result := range instr.Results {
				values[i] = fr.value(result).LLVMValue()
			}
			fr.builder.CreateAggregateRet(values)
		}

	case *ssa.RunDefers:
		fr.builder.CreateCall(fr.runtime.rundefers.LLVMValue(), nil, "")

	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),
			}
		}
		fr.env[instr] = fr.chanSelect(states, instr.Blocking)

	case *ssa.Send:
		fr.chanSend(fr.value(instr.Chan), fr.value(instr.X))

	case *ssa.Slice:
		x := fr.value(instr.X)
		low := fr.value(instr.Low)
		high := fr.value(instr.High)
		fr.env[instr] = fr.slice(x, low, high)

	case *ssa.Store:
		addr := fr.value(instr.Addr).LLVMValue()
		value := fr.value(instr.Val).LLVMValue()
		// The bitcast is necessary to handle recursive pointer stores.
		addr = fr.builder.CreateBitCast(addr, llvm.PointerType(value.Type(), 0), "")
		fr.builder.CreateStore(value, addr)

	case *ssa.TypeAssert:
		x := fr.value(instr.X)
		if iface, ok := x.Type().Underlying().(*types.Interface); ok && iface.NumMethods() > 0 {
			x = x.convertI2E()
		}
		if !instr.CommaOk {
			if _, ok := instr.AssertedType.Underlying().(*types.Interface); ok {
				fr.env[instr] = x.mustConvertE2I(instr.AssertedType)
			} else {
				fr.env[instr] = x.mustConvertE2V(instr.AssertedType)
			}
		} else {
			var result, success *LLVMValue
			if _, ok := instr.AssertedType.Underlying().(*types.Interface); ok {
				result, success = x.convertE2I(instr.AssertedType)
			} else {
				result, success = x.convertE2V(instr.AssertedType)
			}
			resultval := result.LLVMValue()
			okval := success.LLVMValue()
			pairtyp := llvm.StructType([]llvm.Type{resultval.Type(), okval.Type()}, false)
			pair := llvm.Undef(pairtyp)
			pair = fr.builder.CreateInsertValue(pair, resultval, 0, "")
			pair = fr.builder.CreateInsertValue(pair, okval, 1, "")
			fr.env[instr] = fr.NewValue(pair, instr.Type())
		}

	case *ssa.UnOp:
		operand := fr.value(instr.X)
		switch instr.Op {
		case token.ARROW:
			fr.env[instr] = fr.chanRecv(operand, instr.CommaOk)
		case token.MUL:
			// The bitcast is necessary to handle recursive pointer loads.
			llptr := fr.builder.CreateBitCast(operand.LLVMValue(), llvm.PointerType(fr.llvmtypes.ToLLVM(instr.Type()), 0), "")
			fr.env[instr] = fr.NewValue(fr.builder.CreateLoad(llptr, ""), instr.Type())
		default:
			fr.env[instr] = operand.UnaryOp(instr.Op).(*LLVMValue)
		}

	default:
		panic(fmt.Sprintf("unhandled: %v", instr))
	}
}
Beispiel #7
0
Datei: ssa.go Projekt: pcc/llgo
func (u *unit) defineFunction(f *ssa.Function) {
	// Nothing to do for functions without bodies.
	if len(f.Blocks) == 0 {
		return
	}

	// Only define functions from this package.
	if f.Pkg == nil {
		if r := f.Signature.Recv(); r != nil && r.Pkg() != nil && r.Pkg() != u.pkg.Object {
			return
		}
	} else if f.Pkg != u.pkg {
		return
	}

	fr := frame{
		unit:   u,
		blocks: make([]llvm.BasicBlock, len(f.Blocks)),
		env:    make(map[ssa.Value]*LLVMValue),
	}

	fr.logf("Define function: %s", f.String())
	llvmFunction := fr.resolveFunction(f).LLVMValue()
	delete(u.undefinedFuncs, f)

	// Functions that call recover must not be inlined, or we
	// can't tell whether the recover call is valid at runtime.
	if f.Recover != nil {
		llvmFunction.AddFunctionAttr(llvm.NoInlineAttribute)
	}

	for i, block := range f.Blocks {
		fr.blocks[i] = llvm.AddBasicBlock(llvmFunction, fmt.Sprintf(".%d.%s", i, block.Comment))
	}
	fr.builder.SetInsertPointAtEnd(fr.blocks[0])

	var paramOffset int
	if len(f.FreeVars) > 0 {
		// Extract captures from the first implicit parameter.
		arg0 := llvmFunction.Param(0)
		for i, fv := range f.FreeVars {
			addressPtr := fr.builder.CreateStructGEP(arg0, i, "")
			address := fr.builder.CreateLoad(addressPtr, "")
			fr.env[fv] = fr.NewValue(address, fv.Type())
		}
		paramOffset++
	}
	for i, param := range f.Params {
		fr.env[param] = fr.NewValue(llvmFunction.Param(i+paramOffset), param.Type())
	}

	// Allocate stack space for locals in the prologue block.
	prologueBlock := llvm.InsertBasicBlock(fr.blocks[0], "prologue")
	fr.builder.SetInsertPointAtEnd(prologueBlock)
	for _, local := range f.Locals {
		typ := fr.llvmtypes.ToLLVM(deref(local.Type()))
		alloca := fr.builder.CreateAlloca(typ, local.Comment)
		u.memsetZero(alloca, llvm.SizeOf(typ))
		value := fr.NewValue(alloca, local.Type())
		fr.env[local] = value
	}

	// Move any allocs relating to named results from the entry block
	// to the prologue block, so they dominate the rundefers and recover
	// blocks.
	//
	// TODO(axw) ask adonovan for a cleaner way of doing this, e.g.
	// have ssa generate an entry block that defines Allocs and related
	// stores, and then a separate block for function body instructions.
	if f.Synthetic == "" {
		if results := f.Signature.Results(); results != nil {
			for i := 0; i < results.Len(); i++ {
				result := results.At(i)
				if result.Name() == "" {
					break
				}
				for i, instr := range f.Blocks[0].Instrs {
					if instr, ok := instr.(*ssa.Alloc); ok && instr.Heap && instr.Pos() == result.Pos() {
						fr.instruction(instr)
						instrs := f.Blocks[0].Instrs
						instrs = append(instrs[:i], instrs[i+1:]...)
						f.Blocks[0].Instrs = instrs
						break
					}
				}
			}
		}
	}

	// If the function contains any defers, we must first call
	// setjmp so we can call rundefers in response to a panic.
	// We can short-circuit the check for defers with
	// f.Recover != nil.
	if f.Recover != nil || hasDefer(f) {
		rdblock := llvm.AddBasicBlock(llvmFunction, "rundefers")
		defers := fr.builder.CreateAlloca(fr.runtime.defers.llvm, "")
		fr.builder.CreateCall(fr.runtime.initdefers.LLVMValue(), []llvm.Value{defers}, "")
		jb := fr.builder.CreateStructGEP(defers, 0, "")
		jb = fr.builder.CreateBitCast(jb, llvm.PointerType(llvm.Int8Type(), 0), "")
		result := fr.builder.CreateCall(fr.runtime.setjmp.LLVMValue(), []llvm.Value{jb}, "")
		result = fr.builder.CreateIsNotNull(result, "")
		fr.builder.CreateCondBr(result, rdblock, fr.blocks[0])
		// We'll only get here via a panic, which must either be
		// recovered or continue panicking up the stack without
		// returning from "rundefers". The recover block may be
		// nil even if we can recover, in which case we just need
		// to return the zero value for each result (if any).
		var recoverBlock llvm.BasicBlock
		if f.Recover != nil {
			recoverBlock = fr.block(f.Recover)
		} else {
			recoverBlock = llvm.AddBasicBlock(llvmFunction, "recover")
			fr.builder.SetInsertPointAtEnd(recoverBlock)
			var nresults int
			results := f.Signature.Results()
			if results != nil {
				nresults = results.Len()
			}
			switch nresults {
			case 0:
				fr.builder.CreateRetVoid()
			case 1:
				fr.builder.CreateRet(llvm.ConstNull(fr.llvmtypes.ToLLVM(results.At(0).Type())))
			default:
				values := make([]llvm.Value, nresults)
				for i := range values {
					values[i] = llvm.ConstNull(fr.llvmtypes.ToLLVM(results.At(i).Type()))
				}
				fr.builder.CreateAggregateRet(values)
			}
		}
		fr.builder.SetInsertPointAtEnd(rdblock)
		fr.builder.CreateCall(fr.runtime.rundefers.LLVMValue(), nil, "")
		fr.builder.CreateBr(recoverBlock)
	} else {
		fr.builder.CreateBr(fr.blocks[0])
	}

	for i, block := range f.Blocks {
		fr.translateBlock(block, fr.blocks[i])
	}
}
Beispiel #8
0
func (c *compiler) VisitGoStmt(stmt *ast.GoStmt) {
	//stmt.Call *ast.CallExpr
	// TODO
	var fn *LLVMValue
	switch x := (stmt.Call.Fun).(type) {
	case *ast.Ident:
		fn = c.Resolve(x.Obj).(*LLVMValue)
		if fn == nil {
			panic(fmt.Sprintf(
				"No function found with name '%s'", x.String()))
		}
	default:
		fn = c.VisitExpr(stmt.Call.Fun).(*LLVMValue)
	}

	// Evaluate arguments, store in a structure on the stack.
	var args_struct_type llvm.Type
	var args_mem llvm.Value
	var args_size llvm.Value
	if stmt.Call.Args != nil {
		param_types := make([]llvm.Type, 0)
		fn_type := types.Deref(fn.Type()).(*types.Func)
		for _, param := range fn_type.Params {
			typ := param.Type.(types.Type)
			param_types = append(param_types, c.types.ToLLVM(typ))
		}
		args_struct_type = llvm.StructType(param_types, false)
		args_mem = c.builder.CreateAlloca(args_struct_type, "")
		for i, expr := range stmt.Call.Args {
			value_i := c.VisitExpr(expr)
			value_i = value_i.Convert(fn_type.Params[i].Type.(types.Type))
			arg_i := c.builder.CreateGEP(args_mem, []llvm.Value{
				llvm.ConstInt(llvm.Int32Type(), 0, false),
				llvm.ConstInt(llvm.Int32Type(), uint64(i), false)}, "")
			c.builder.CreateStore(value_i.LLVMValue(), arg_i)
		}
		args_size = llvm.SizeOf(args_struct_type)
		args_size = llvm.ConstTrunc(args_size, llvm.Int32Type())
	} else {
		args_struct_type = llvm.VoidType()
		args_mem = llvm.ConstNull(llvm.PointerType(args_struct_type, 0))
		args_size = llvm.ConstInt(llvm.Int32Type(), 0, false)
	}

	// When done, return to where we were.
	defer c.builder.SetInsertPointAtEnd(c.builder.GetInsertBlock())

	// Create a function that will take a pointer to a structure of the type
	// defined above, or no parameters if there are none to pass.
	indirect_fn_type := llvm.FunctionType(
		llvm.VoidType(),
		[]llvm.Type{llvm.PointerType(args_struct_type, 0)}, false)
	indirect_fn := llvm.AddFunction(c.module.Module, "", indirect_fn_type)
	indirect_fn.SetFunctionCallConv(llvm.CCallConv)

	// Call "newgoroutine" with the indirect function and stored args.
	newgoroutine := getnewgoroutine(c.module.Module)
	ngr_param_types := newgoroutine.Type().ElementType().ParamTypes()
	fn_arg := c.builder.CreateBitCast(indirect_fn, ngr_param_types[0], "")
	args_arg := c.builder.CreateBitCast(args_mem,
		llvm.PointerType(llvm.Int8Type(), 0), "")
	c.builder.CreateCall(newgoroutine,
		[]llvm.Value{fn_arg, args_arg, args_size}, "")

	entry := llvm.AddBasicBlock(indirect_fn, "entry")
	c.builder.SetInsertPointAtEnd(entry)
	var args []llvm.Value
	if stmt.Call.Args != nil {
		args_mem = indirect_fn.Param(0)
		args = make([]llvm.Value, len(stmt.Call.Args))
		for i := range stmt.Call.Args {
			arg_i := c.builder.CreateGEP(args_mem, []llvm.Value{
				llvm.ConstInt(llvm.Int32Type(), 0, false),
				llvm.ConstInt(llvm.Int32Type(), uint64(i), false)}, "")
			args[i] = c.builder.CreateLoad(arg_i, "")
		}
	}
	c.builder.CreateCall(fn.LLVMValue(), args, "")
	c.builder.CreateRetVoid()
}
Beispiel #9
0
func (c *compiler) chanSelect(states []selectState, blocking bool) *LLVMValue {
	stackptr := c.stacksave()
	defer c.stackrestore(stackptr)

	n := uint64(len(states))
	if !blocking {
		// blocking means there's no default case
		n++
	}
	lln := llvm.ConstInt(llvm.Int32Type(), n, false)
	allocsize := c.builder.CreateCall(c.runtime.selectsize.LLVMValue(), []llvm.Value{lln}, "")
	selectp := c.builder.CreateArrayAlloca(llvm.Int8Type(), allocsize, "selectp")
	c.memsetZero(selectp, allocsize)
	selectp = c.builder.CreatePtrToInt(selectp, c.target.IntPtrType(), "")
	c.builder.CreateCall(c.runtime.selectinit.LLVMValue(), []llvm.Value{lln, selectp}, "")

	// Allocate stack for the values to send/receive.
	//
	// TODO(axw) request optimisation in ssa to special-
	// case receive cases with no assignment, so we know
	// not to allocate stack space or do a copy.
	resTypes := []types.Type{types.Typ[types.Int], types.Typ[types.Bool]}
	for _, state := range states {
		if state.Dir == types.RecvOnly {
			chantyp := state.Chan.Type().Underlying().(*types.Chan)
			resTypes = append(resTypes, chantyp.Elem())
		}
	}
	resType := tupleType(resTypes...)
	llResType := c.types.ToLLVM(resType)
	tupleptr := c.builder.CreateAlloca(llResType, "")
	c.memsetZero(tupleptr, llvm.SizeOf(llResType))

	var recvindex int
	ptrs := make([]llvm.Value, len(states))
	for i, state := range states {
		chantyp := state.Chan.Type().Underlying().(*types.Chan)
		elemtyp := c.types.ToLLVM(chantyp.Elem())
		if state.Dir == types.SendOnly {
			ptrs[i] = c.builder.CreateAlloca(elemtyp, "")
			c.builder.CreateStore(state.Send.LLVMValue(), ptrs[i])
		} else {
			ptrs[i] = c.builder.CreateStructGEP(tupleptr, recvindex+2, "")
			recvindex++
		}
		ptrs[i] = c.builder.CreatePtrToInt(ptrs[i], c.target.IntPtrType(), "")
	}

	// Create select{send,recv} calls.
	selectsend := c.runtime.selectsend.LLVMValue()
	selectrecv := c.runtime.selectrecv.LLVMValue()
	var received llvm.Value
	if recvindex > 0 {
		received = c.builder.CreateStructGEP(tupleptr, 1, "")
	}
	if !blocking {
		c.builder.CreateCall(c.runtime.selectdefault.LLVMValue(), []llvm.Value{selectp}, "")
	}
	for i, state := range states {
		ch := state.Chan.LLVMValue()
		if state.Dir == types.SendOnly {
			c.builder.CreateCall(selectsend, []llvm.Value{selectp, ch, ptrs[i]}, "")
		} else {
			c.builder.CreateCall(selectrecv, []llvm.Value{selectp, ch, ptrs[i], received}, "")
		}
	}

	// Fire off the select.
	index := c.builder.CreateCall(c.runtime.selectgo.LLVMValue(), []llvm.Value{selectp}, "")
	tuple := c.builder.CreateLoad(tupleptr, "")
	tuple = c.builder.CreateInsertValue(tuple, index, 0, "")
	return c.NewValue(tuple, resType)
}
Beispiel #10
0
func (c *compiler) createTypeMalloc(t llvm.Type) llvm.Value {
	ptr := c.createMalloc(llvm.SizeOf(t))
	return c.builder.CreateIntToPtr(ptr, llvm.PointerType(t, 0), "")
}
Beispiel #11
0
func (c *compiler) VisitValueSpec(valspec *ast.ValueSpec, isconst bool) {
	// Check if the value-spec has already been visited (referenced
	// before definition visited.)
	if len(valspec.Names) > 0 {
		if _, ok := valspec.Names[0].Obj.Data.(Value); ok {
			return
		}
	}

	// Constants are evaluated during typechecking. We can just pull
	// out the value from the name's object data.
	if isconst {
		for _, name := range valspec.Names {
			if name.Name != "_" && name.Obj != nil {
				value := name.Obj.Data.(types.Const)
				typ := name.Obj.Type.(types.Type)
				name.Obj.Data = ConstValue{value, c, typ}
			}
		}
		return
	}

	pkgname, ispackagelevel := c.pkgmap[valspec.Names[0].Obj]
	if ispackagelevel {
		c.createGlobals(valspec.Names, valspec.Values, pkgname)
		return
	}

	var values []Value
	if len(valspec.Values) == 1 && len(valspec.Names) > 1 {
		values = c.destructureExpr(valspec.Values[0])
	} else if len(valspec.Values) > 0 {
		values = make([]Value, len(valspec.Names))
		for i := range valspec.Names {
			values[i] = c.VisitExpr(valspec.Values[i])
		}
	}

	for i, name := range valspec.Names {
		if name.Name == "_" {
			continue
		}

		// The variable should be allocated on the stack if it's
		// declared inside a function.
		//
		// FIXME currently allocating all variables on the heap.
		// Change this to allocate on the stack, and perform
		// escape analysis to determine whether to promote.
		typ := name.Obj.Type.(types.Type)
		llvmtyp := c.types.ToLLVM(typ)
		ptr := c.createTypeMalloc(llvmtyp)
		if values == nil || values[i] == nil {
			// If no initialiser was specified, bzero it.
			bzero := c.NamedFunction("runtime.bzero", "func f(unsafe.Pointer, uintptr)")
			ptr := c.builder.CreatePtrToInt(ptr, c.target.IntPtrType(), "")
			args := []llvm.Value{ptr, llvm.SizeOf(llvmtyp)}
			c.builder.CreateCall(bzero, args, "")
		} else {
			// FIXME we need to revisit how aggregate types
			// are initialised/copied/etc. A CreateStore will
			// try to do everything in registers, which is
			// going to hurt when the aggregate is large.
			llvmInit := values[i].Convert(typ).LLVMValue()
			c.builder.CreateStore(llvmInit, ptr)
		}
		stackvar := c.NewLLVMValue(ptr, &types.Pointer{Base: typ}).makePointee()
		stackvar.stack = c.functions[len(c.functions)-1]
		name.Obj.Data = stackvar
	}
}
Beispiel #12
0
func (c *compiler) VisitCompositeLit(lit *ast.CompositeLit) Value {
	typ := c.types.expr[lit]
	var valuemap map[interface{}]Value
	var valuelist []Value
	_, isstruct := types.Underlying(typ).(*types.Struct)
	if lit.Elts != nil {
		for _, elt := range lit.Elts {
			var value Value
			if kv, iskv := elt.(*ast.KeyValueExpr); iskv {
				value = c.VisitExpr(kv.Value)
				if valuemap == nil {
					valuemap = make(map[interface{}]Value)
				}
				var key interface{}
				if isstruct {
					key = kv.Key.(*ast.Ident).Name
				} else {
					key = c.VisitExpr(kv.Key)
				}
				valuemap[key] = value
			} else {
				value = c.VisitExpr(elt)
				valuelist = append(valuelist, value)
			}
		}
	}

	// For array/slice types, convert key:value to contiguous
	// values initialiser.
	switch types.Underlying(typ).(type) {
	case *types.Array, *types.Slice:
		if len(valuemap) > 0 {
			maxi := int64(-1)
			for key, _ := range valuemap {
				i := key.(ConstValue).Int64()
				if i < 0 {
					panic("array index must be non-negative integer constant")
				} else if i > maxi {
					maxi = i
				}
			}
			valuelist = make([]Value, maxi+1)
			for key, value := range valuemap {
				i := key.(ConstValue).Int64()
				valuelist[i] = value
			}
		}
	}

	origtyp := typ
	switch typ := types.Underlying(typ).(type) {
	case *types.Array:
		elttype := typ.Elt
		llvmelttype := c.types.ToLLVM(elttype)
		llvmvalues := make([]llvm.Value, typ.Len)
		for i := range llvmvalues {
			var value Value
			if i < len(valuelist) {
				value = valuelist[i]
			}
			if value == nil {
				llvmvalues[i] = llvm.ConstNull(llvmelttype)
			} else if _, ok := value.(ConstValue); ok || value.LLVMValue().IsConstant() {
				llvmvalues[i] = value.Convert(elttype).LLVMValue()
			} else {
				llvmvalues[i] = llvm.Undef(llvmelttype)
			}
		}
		array := llvm.ConstArray(llvmelttype, llvmvalues)
		for i, value := range valuelist {
			if llvmvalues[i].IsUndef() {
				value := value.Convert(elttype).LLVMValue()
				array = c.builder.CreateInsertValue(array, value, i, "")
			}
		}
		return c.NewLLVMValue(array, origtyp)

	case *types.Slice:
		ptr := c.createTypeMalloc(c.types.ToLLVM(typ))

		eltType := c.types.ToLLVM(typ.Elt)
		arrayType := llvm.ArrayType(eltType, len(valuelist))
		valuesPtr := c.createMalloc(llvm.SizeOf(arrayType))
		valuesPtr = c.builder.CreateIntToPtr(valuesPtr, llvm.PointerType(eltType, 0), "")

		//valuesPtr = c.builder.CreateBitCast(valuesPtr, llvm.PointerType(valuesPtr.Type(), 0), "")
		// TODO check result of mallocs
		length := llvm.ConstInt(llvm.Int32Type(), uint64(len(valuelist)), false)
		c.builder.CreateStore(valuesPtr, c.builder.CreateStructGEP(ptr, 0, "")) // data
		c.builder.CreateStore(length, c.builder.CreateStructGEP(ptr, 1, ""))    // len
		c.builder.CreateStore(length, c.builder.CreateStructGEP(ptr, 2, ""))    // cap
		null := llvm.ConstNull(c.types.ToLLVM(typ.Elt))
		for i, value := range valuelist {
			index := llvm.ConstInt(llvm.Int32Type(), uint64(i), false)
			valuePtr := c.builder.CreateGEP(valuesPtr, []llvm.Value{index}, "")
			if value == nil {
				c.builder.CreateStore(null, valuePtr)
			} else {
				c.builder.CreateStore(value.Convert(typ.Elt).LLVMValue(), valuePtr)
			}
		}
		m := c.NewLLVMValue(ptr, &types.Pointer{Base: origtyp})
		return m.makePointee()

	case *types.Struct:
		values := valuelist
		llvmtyp := c.types.ToLLVM(typ)
		ptr := c.createTypeMalloc(llvmtyp)

		bzero := c.NamedFunction("runtime.bzero", "func f(unsafe.Pointer, uintptr)")
		ptrintval := c.builder.CreatePtrToInt(ptr, c.target.IntPtrType(), "")
		args := []llvm.Value{ptrintval, llvm.SizeOf(llvmtyp)}
		c.builder.CreateCall(bzero, args, "")

		if valuemap != nil {
			for key, value := range valuemap {
				fieldName := key.(string)
				index := typ.FieldIndices[fieldName]
				for len(values) <= int(index) {
					values = append(values, nil)
				}
				values[index] = value
			}
		}
		for i, value := range values {
			if value != nil {
				elttype := typ.Fields[i].Type.(types.Type)
				llvm_value := value.Convert(elttype).LLVMValue()
				ptr := c.builder.CreateStructGEP(ptr, i, "")
				c.builder.CreateStore(llvm_value, ptr)
			}
		}
		m := c.NewLLVMValue(ptr, &types.Pointer{Base: origtyp})
		return m.makePointee()

	case *types.Map:
		value := llvm.ConstNull(c.types.ToLLVM(typ))
		// TODO initialise map
		return c.NewLLVMValue(value, origtyp)
	}
	panic(fmt.Sprint("Unhandled type kind: ", typ))
}
Beispiel #13
0
func (c *compiler) VisitCompositeLit(lit *ast.CompositeLit) (v *LLVMValue) {
	typ := c.types.expr[lit].Type
	var valuemap map[interface{}]Value
	var valuelist []Value

	if ptr, ok := typ.(*types.Pointer); ok {
		typ = ptr.Elem()
		defer func() {
			v = v.pointer
		}()
	}

	var isstruct, isarray, isslice, ismap bool
	switch typ.Underlying().(type) {
	case *types.Struct:
		isstruct = true
	case *types.Array:
		isarray = true
	case *types.Slice:
		isslice = true
	case *types.Map:
		ismap = true
	default:
		panic(fmt.Errorf("Unhandled type: %s", typ))
	}

	if lit.Elts != nil {
		for i, elt := range lit.Elts {
			if kv, iskv := elt.(*ast.KeyValueExpr); iskv {
				if valuemap == nil {
					valuemap = make(map[interface{}]Value)
				}
				var key interface{}
				var elttyp types.Type
				switch {
				case isstruct:
					name := kv.Key.(*ast.Ident).Name
					key = name
					typ := typ.Underlying().(*types.Struct)
					elttyp = typ.Field(fieldIndex(typ, name)).Type
				case isarray:
					key = c.types.expr[kv.Key].Value
					typ := typ.Underlying().(*types.Array)
					elttyp = typ.Elem()
				case isslice:
					key = c.types.expr[kv.Key].Value
					typ := typ.Underlying().(*types.Slice)
					elttyp = typ.Elem()
				case ismap:
					key = c.VisitExpr(kv.Key)
					typ := typ.Underlying().(*types.Map)
					elttyp = typ.Elem()
				default:
					panic("unreachable")
				}
				c.convertUntyped(kv.Value, elttyp)
				valuemap[key] = c.VisitExpr(kv.Value)
			} else {
				switch {
				case isstruct:
					typ := typ.Underlying().(*types.Struct)
					c.convertUntyped(elt, typ.Field(i).Type)
				case isarray:
					typ := typ.Underlying().(*types.Array)
					c.convertUntyped(elt, typ.Elem())
				case isslice:
					typ := typ.Underlying().(*types.Slice)
					c.convertUntyped(elt, typ.Elem())
				}
				value := c.VisitExpr(elt)
				valuelist = append(valuelist, value)
			}
		}
	}

	// For array/slice types, convert key:value to contiguous
	// values initialiser.
	switch typ.Underlying().(type) {
	case *types.Array, *types.Slice:
		if len(valuemap) > 0 {
			var maxkey uint64
			for key, _ := range valuemap {
				key, _ := exact.Uint64Val(key.(exact.Value))
				if key > maxkey {
					maxkey = key
				}
			}
			valuelist = make([]Value, maxkey+1)
			for key, value := range valuemap {
				key, _ := exact.Uint64Val(key.(exact.Value))
				valuelist[key] = value
			}
		}
	}

	origtyp := typ
	switch typ := typ.Underlying().(type) {
	case *types.Array:
		elttype := typ.Elem()
		llvmelttype := c.types.ToLLVM(elttype)
		llvmvalues := make([]llvm.Value, typ.Len())
		for i := range llvmvalues {
			var value Value
			if i < len(valuelist) {
				value = valuelist[i]
			}
			if value == nil {
				llvmvalues[i] = llvm.ConstNull(llvmelttype)
			} else if value.LLVMValue().IsConstant() {
				llvmvalues[i] = value.Convert(elttype).LLVMValue()
			} else {
				llvmvalues[i] = llvm.Undef(llvmelttype)
			}
		}
		array := llvm.ConstArray(llvmelttype, llvmvalues)
		for i, value := range valuelist {
			if llvmvalues[i].IsUndef() {
				value := value.Convert(elttype).LLVMValue()
				array = c.builder.CreateInsertValue(array, value, i, "")
			}
		}
		return c.NewValue(array, origtyp)

	case *types.Slice:
		ptr := c.createTypeMalloc(c.types.ToLLVM(typ))

		eltType := c.types.ToLLVM(typ.Elem())
		arrayType := llvm.ArrayType(eltType, len(valuelist))
		valuesPtr := c.createMalloc(llvm.SizeOf(arrayType))
		valuesPtr = c.builder.CreateIntToPtr(valuesPtr, llvm.PointerType(eltType, 0), "")

		//valuesPtr = c.builder.CreateBitCast(valuesPtr, llvm.PointerType(valuesPtr.Type(), 0), "")
		length := llvm.ConstInt(c.types.inttype, uint64(len(valuelist)), false)
		c.builder.CreateStore(valuesPtr, c.builder.CreateStructGEP(ptr, 0, "")) // data
		c.builder.CreateStore(length, c.builder.CreateStructGEP(ptr, 1, ""))    // len
		c.builder.CreateStore(length, c.builder.CreateStructGEP(ptr, 2, ""))    // cap
		null := llvm.ConstNull(c.types.ToLLVM(typ.Elem()))
		for i, value := range valuelist {
			index := llvm.ConstInt(llvm.Int32Type(), uint64(i), false)
			valuePtr := c.builder.CreateGEP(valuesPtr, []llvm.Value{index}, "")
			if value == nil {
				c.builder.CreateStore(null, valuePtr)
			} else {
				c.builder.CreateStore(value.Convert(typ.Elem()).LLVMValue(), valuePtr)
			}
		}
		m := c.NewValue(ptr, types.NewPointer(origtyp))
		return m.makePointee()

	case *types.Struct:
		values := valuelist
		llvmtyp := c.types.ToLLVM(typ)
		ptr := c.createTypeMalloc(llvmtyp)

		if valuemap != nil {
			for key, value := range valuemap {
				index := fieldIndex(typ, key.(string))
				for len(values) <= index {
					values = append(values, nil)
				}
				values[index] = value
			}
		}
		for i, value := range values {
			if value != nil {
				elttype := typ.Field(i).Type
				llvm_value := value.Convert(elttype).LLVMValue()
				ptr := c.builder.CreateStructGEP(ptr, i, "")
				c.builder.CreateStore(llvm_value, ptr)
			}
		}
		m := c.NewValue(ptr, types.NewPointer(origtyp))
		return m.makePointee()

	case *types.Map:
		value := llvm.ConstNull(c.types.ToLLVM(typ))
		// TODO initialise map
		return c.NewValue(value, origtyp)
	}
	panic(fmt.Sprint("Unhandled type kind: ", typ))
}