예제 #1
0
파일: ssa.go 프로젝트: hinike/llgo
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))
	}
}
예제 #2
0
파일: ssa.go 프로젝트: hinike/llgo
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()
	}
}