Beispiel #1
0
// 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
}
Beispiel #2
0
// mapIterInit creates a map iterator
func (fr *frame) mapIterInit(m *govalue) []*govalue {
	// We represent an iterator as a tuple (map, *bool). The second element
	// controls whether the code we generate for "next" (below) calls the
	// runtime function for the first or the next element. We let the
	// optimizer reorganize this into something more sensible.
	isinit := fr.allocaBuilder.CreateAlloca(llvm.Int1Type(), "")
	fr.builder.CreateStore(llvm.ConstNull(llvm.Int1Type()), isinit)

	return []*govalue{m, newValue(isinit, types.NewPointer(types.Typ[types.Bool]))}
}
Beispiel #3
0
func (fr *frame) callRecover(isDeferredRecover bool) *govalue {
	startbb := fr.builder.GetInsertBlock()
	recoverbb := llvm.AddBasicBlock(fr.function, "")
	contbb := llvm.AddBasicBlock(fr.function, "")
	canRecover := fr.builder.CreateTrunc(fr.canRecover, llvm.Int1Type(), "")
	fr.builder.CreateCondBr(canRecover, recoverbb, contbb)

	fr.builder.SetInsertPointAtEnd(recoverbb)
	var recovered llvm.Value
	if isDeferredRecover {
		recovered = fr.runtime.deferredRecover.call(fr)[0]
	} else {
		recovered = fr.runtime.recover.call(fr)[0]
	}
	recoverbb = fr.builder.GetInsertBlock()
	fr.builder.CreateBr(contbb)

	fr.builder.SetInsertPointAtEnd(contbb)
	eface := types.NewInterface(nil, nil)
	llv := fr.builder.CreatePHI(fr.types.ToLLVM(eface), "")
	llv.AddIncoming(
		[]llvm.Value{llvm.ConstNull(llv.Type()), recovered},
		[]llvm.BasicBlock{startbb, recoverbb},
	)
	return newValue(llv, eface)
}
Beispiel #4
0
func (fr *frame) memcpy(dest llvm.Value, src llvm.Value, size llvm.Value) {
	memcpy := fr.runtime.memcpy
	dest = fr.builder.CreateBitCast(dest, llvm.PointerType(llvm.Int8Type(), 0), "")
	src = fr.builder.CreateBitCast(src, llvm.PointerType(llvm.Int8Type(), 0), "")
	size = fr.createZExtOrTrunc(size, fr.target.IntPtrType(), "")
	align := llvm.ConstInt(llvm.Int32Type(), 1, false)
	isvolatile := llvm.ConstNull(llvm.Int1Type())
	fr.builder.CreateCall(memcpy, []llvm.Value{dest, src, size, align, isvolatile}, "")
}
Beispiel #5
0
func (fr *frame) memsetZero(ptr llvm.Value, size llvm.Value) {
	memset := fr.runtime.memset
	ptr = fr.builder.CreateBitCast(ptr, llvm.PointerType(llvm.Int8Type(), 0), "")
	fill := llvm.ConstNull(llvm.Int8Type())
	size = fr.createZExtOrTrunc(size, fr.target.IntPtrType(), "")
	align := llvm.ConstInt(llvm.Int32Type(), 1, false)
	isvolatile := llvm.ConstNull(llvm.Int1Type())
	fr.builder.CreateCall(memset, []llvm.Value{ptr, fill, size, align, isvolatile}, "")
}
Beispiel #6
0
func (fr *frame) interfaceTypeCheck(val *govalue, ty types.Type) (v *govalue, okval *govalue) {
	tytd := fr.types.ToRuntime(ty)
	if _, ok := ty.Underlying().(*types.Interface); ok {
		var result []llvm.Value
		if val.Type().Underlying().(*types.Interface).NumMethods() > 0 {
			result = fr.runtime.ifaceI2I2.call(fr, tytd, val.value)
		} else {
			result = fr.runtime.ifaceE2I2.call(fr, tytd, val.value)
		}
		v = newValue(result[0], ty)
		okval = newValue(result[1], types.Typ[types.Bool])
	} else {
		valtd := fr.getInterfaceTypeDescriptor(val)
		tyequal := fr.runtime.typeDescriptorsEqual.call(fr, valtd, tytd)[0]
		okval = newValue(tyequal, types.Typ[types.Bool])
		tyequal = fr.builder.CreateTrunc(tyequal, llvm.Int1Type(), "")

		v = fr.getInterfaceValueOrNull(tyequal, val, ty)
	}
	return
}
Beispiel #7
0
func newRuntimeInterface(module llvm.Module, tm *llvmTypeMap) (*runtimeInterface, error) {
	var ri runtimeInterface

	Bool := types.Typ[types.Bool]
	Complex128 := types.Typ[types.Complex128]
	Float64 := types.Typ[types.Float64]
	Int32 := types.Typ[types.Int32]
	Int64 := types.Typ[types.Int64]
	Int := types.Typ[types.Int]
	Rune := types.Typ[types.Rune]
	String := types.Typ[types.String]
	Uintptr := types.Typ[types.Uintptr]
	UnsafePointer := types.Typ[types.UnsafePointer]

	EmptyInterface := types.NewInterface(nil, nil)
	IntSlice := types.NewSlice(types.Typ[types.Int])

	for _, rt := range [...]struct {
		name      string
		rfi       *runtimeFnInfo
		args, res []types.Type
		attrs     []llvm.Attribute
	}{
		{
			name: "__go_append",
			rfi:  &ri.append,
			args: []types.Type{IntSlice, UnsafePointer, Uintptr, Uintptr},
			res:  []types.Type{IntSlice},
		},
		{
			name: "__go_assert_interface",
			rfi:  &ri.assertInterface,
			args: []types.Type{UnsafePointer, UnsafePointer},
			res:  []types.Type{UnsafePointer},
		},
		{
			name: "__go_can_recover",
			rfi:  &ri.canRecover,
			args: []types.Type{UnsafePointer},
			res:  []types.Type{Bool},
		},
		{
			name: "__go_chan_cap",
			rfi:  &ri.chanCap,
			args: []types.Type{UnsafePointer},
			res:  []types.Type{Int},
		},
		{
			name: "__go_chan_len",
			rfi:  &ri.chanLen,
			args: []types.Type{UnsafePointer},
			res:  []types.Type{Int},
		},
		{
			name: "runtime.chanrecv2",
			rfi:  &ri.chanrecv2,
			args: []types.Type{UnsafePointer, UnsafePointer, UnsafePointer},
			res:  []types.Type{Bool},
		},
		{
			name: "__go_check_defer",
			rfi:  &ri.checkDefer,
			args: []types.Type{UnsafePointer},
		},
		{
			name: "__go_check_interface_type",
			rfi:  &ri.checkInterfaceType,
			args: []types.Type{UnsafePointer, UnsafePointer, UnsafePointer},
		},
		{
			name: "__go_builtin_close",
			rfi:  &ri.builtinClose,
			args: []types.Type{UnsafePointer},
		},
		{
			name: "__go_convert_interface",
			rfi:  &ri.convertInterface,
			args: []types.Type{UnsafePointer, UnsafePointer},
			res:  []types.Type{UnsafePointer},
		},
		{
			name: "__go_copy",
			rfi:  &ri.copy,
			args: []types.Type{UnsafePointer, UnsafePointer, Uintptr},
		},
		{
			name: "__go_defer",
			rfi:  &ri.Defer,
			args: []types.Type{UnsafePointer, UnsafePointer, UnsafePointer},
		},
		{
			name: "__go_deferred_recover",
			rfi:  &ri.deferredRecover,
			res:  []types.Type{EmptyInterface},
		},
		{
			name: "__go_empty_interface_compare",
			rfi:  &ri.emptyInterfaceCompare,
			args: []types.Type{EmptyInterface, EmptyInterface},
			res:  []types.Type{Int},
		},
		{
			name: "__go_go",
			rfi:  &ri.Go,
			args: []types.Type{UnsafePointer, UnsafePointer},
		},
		{
			name: "runtime.ifaceE2I2",
			rfi:  &ri.ifaceE2I2,
			args: []types.Type{UnsafePointer, EmptyInterface},
			res:  []types.Type{EmptyInterface, Bool},
		},
		{
			name: "runtime.ifaceI2I2",
			rfi:  &ri.ifaceI2I2,
			args: []types.Type{UnsafePointer, EmptyInterface},
			res:  []types.Type{EmptyInterface, Bool},
		},
		{
			name: "__go_int_array_to_string",
			rfi:  &ri.intArrayToString,
			args: []types.Type{UnsafePointer, Int},
			res:  []types.Type{String},
		},
		{
			name: "__go_int_to_string",
			rfi:  &ri.intToString,
			args: []types.Type{Int},
			res:  []types.Type{String},
		},
		{
			name: "__go_interface_compare",
			rfi:  &ri.interfaceCompare,
			args: []types.Type{EmptyInterface, EmptyInterface},
			res:  []types.Type{Int},
		},
		{
			name: "__go_make_slice2",
			rfi:  &ri.makeSlice,
			args: []types.Type{UnsafePointer, Uintptr, Uintptr},
			res:  []types.Type{IntSlice},
		},
		{
			name: "runtime.mapdelete",
			rfi:  &ri.mapdelete,
			args: []types.Type{UnsafePointer, UnsafePointer},
		},
		{
			name: "runtime.mapiter2",
			rfi:  &ri.mapiter2,
			args: []types.Type{UnsafePointer, UnsafePointer, UnsafePointer},
		},
		{
			name: "runtime.mapiterinit",
			rfi:  &ri.mapiterinit,
			args: []types.Type{UnsafePointer, UnsafePointer},
		},
		{
			name: "runtime.mapiternext",
			rfi:  &ri.mapiternext,
			args: []types.Type{UnsafePointer},
		},
		{
			name: "__go_map_index",
			rfi:  &ri.mapIndex,
			args: []types.Type{UnsafePointer, UnsafePointer, Bool},
			res:  []types.Type{UnsafePointer},
		},
		{
			name: "__go_map_len",
			rfi:  &ri.mapLen,
			args: []types.Type{UnsafePointer},
			res:  []types.Type{Int},
		},
		{
			name: "__go_new",
			rfi:  &ri.New,
			args: []types.Type{UnsafePointer, Uintptr},
			res:  []types.Type{UnsafePointer},
		},
		{
			name: "__go_new_channel",
			rfi:  &ri.newChannel,
			args: []types.Type{UnsafePointer, Uintptr},
			res:  []types.Type{UnsafePointer},
		},
		{
			name: "__go_new_map",
			rfi:  &ri.newMap,
			args: []types.Type{UnsafePointer, Uintptr},
			res:  []types.Type{UnsafePointer},
		},
		{
			name: "__go_new_nopointers",
			rfi:  &ri.NewNopointers,
			args: []types.Type{UnsafePointer, Uintptr},
			res:  []types.Type{UnsafePointer},
		},
		{
			name: "runtime.newselect",
			rfi:  &ri.newSelect,
			args: []types.Type{Int32},
			res:  []types.Type{UnsafePointer},
		},
		{
			name:  "__go_panic",
			rfi:   &ri.panic,
			args:  []types.Type{EmptyInterface},
			attrs: []llvm.Attribute{llvm.NoReturnAttribute},
		},
		{
			name: "__go_print_bool",
			rfi:  &ri.printBool,
			args: []types.Type{Bool},
		},
		{
			name: "__go_print_complex",
			rfi:  &ri.printComplex,
			args: []types.Type{Complex128},
		},
		{
			name: "__go_print_double",
			rfi:  &ri.printDouble,
			args: []types.Type{Float64},
		},
		{
			name: "__go_print_empty_interface",
			rfi:  &ri.printEmptyInterface,
			args: []types.Type{EmptyInterface},
		},
		{
			name: "__go_print_interface",
			rfi:  &ri.printInterface,
			args: []types.Type{EmptyInterface},
		},
		{
			name: "__go_print_int64",
			rfi:  &ri.printInt64,
			args: []types.Type{Int64},
		},
		{
			name: "__go_print_nl",
			rfi:  &ri.printNl,
		},
		{
			name: "__go_print_pointer",
			rfi:  &ri.printPointer,
			args: []types.Type{UnsafePointer},
		},
		{
			name: "__go_print_slice",
			rfi:  &ri.printSlice,
			args: []types.Type{IntSlice},
		},
		{
			name: "__go_print_space",
			rfi:  &ri.printSpace,
		},
		{
			name: "__go_print_string",
			rfi:  &ri.printString,
			args: []types.Type{String},
		},
		{
			name: "__go_print_uint64",
			rfi:  &ri.printUint64,
			args: []types.Type{Int64},
		},
		{
			name: "__go_receive",
			rfi:  &ri.receive,
			args: []types.Type{UnsafePointer, UnsafePointer, UnsafePointer},
		},
		{
			name: "__go_recover",
			rfi:  &ri.recover,
			res:  []types.Type{EmptyInterface},
		},
		{
			name: "__go_register_gc_roots",
			rfi:  &ri.registerGcRoots,
			args: []types.Type{UnsafePointer},
		},
		{
			name:  "__go_runtime_error",
			rfi:   &ri.runtimeError,
			args:  []types.Type{Int32},
			attrs: []llvm.Attribute{llvm.NoReturnAttribute},
		},
		{
			name: "runtime.selectdefault",
			rfi:  &ri.selectdefault,
			args: []types.Type{UnsafePointer, Int32},
		},
		{
			name: "runtime.selectgo",
			rfi:  &ri.selectgo,
			args: []types.Type{UnsafePointer},
			res:  []types.Type{Int},
		},
		{
			name: "runtime.selectrecv2",
			rfi:  &ri.selectrecv2,
			args: []types.Type{UnsafePointer, UnsafePointer, UnsafePointer, UnsafePointer, Int32},
		},
		{
			name: "runtime.selectsend",
			rfi:  &ri.selectsend,
			args: []types.Type{UnsafePointer, UnsafePointer, UnsafePointer, Int32},
		},
		{
			name: "__go_send_big",
			rfi:  &ri.sendBig,
			args: []types.Type{UnsafePointer, UnsafePointer, UnsafePointer},
		},
		{
			name: "__go_set_defer_retaddr",
			rfi:  &ri.setDeferRetaddr,
			args: []types.Type{UnsafePointer},
			res:  []types.Type{Bool},
		},
		{
			name: "__go_strcmp",
			rfi:  &ri.strcmp,
			args: []types.Type{String, String},
			res:  []types.Type{Int},
		},
		{
			name: "__go_string_plus",
			rfi:  &ri.stringPlus,
			args: []types.Type{String, String},
			res:  []types.Type{String},
		},
		{
			name: "__go_string_slice",
			rfi:  &ri.stringSlice,
			args: []types.Type{String, Int, Int},
			res:  []types.Type{String},
		},
		{
			name: "__go_string_to_int_array",
			rfi:  &ri.stringToIntArray,
			args: []types.Type{String},
			res:  []types.Type{IntSlice},
		},
		{
			name: "runtime.stringiter2",
			rfi:  &ri.stringiter2,
			args: []types.Type{String, Int},
			res:  []types.Type{Int, Rune},
		},
		{
			name: "__go_type_descriptors_equal",
			rfi:  &ri.typeDescriptorsEqual,
			args: []types.Type{UnsafePointer, UnsafePointer},
			res:  []types.Type{Bool},
		},
		{
			name: "__go_undefer",
			rfi:  &ri.undefer,
			args: []types.Type{UnsafePointer},
		},
	} {
		rt.rfi.init(tm, module, rt.name, rt.args, rt.res)
		for _, attr := range rt.attrs {
			rt.rfi.fn.AddFunctionAttr(attr)
		}
	}

	memsetName := "llvm.memset.p0i8.i" + strconv.Itoa(tm.target.IntPtrType().IntTypeWidth())
	memsetType := llvm.FunctionType(
		llvm.VoidType(),
		[]llvm.Type{
			llvm.PointerType(llvm.Int8Type(), 0),
			llvm.Int8Type(),
			tm.target.IntPtrType(),
			llvm.Int32Type(),
			llvm.Int1Type(),
		},
		false,
	)
	ri.memset = llvm.AddFunction(module, memsetName, memsetType)

	memcpyName := "llvm.memcpy.p0i8.p0i8.i" + strconv.Itoa(tm.target.IntPtrType().IntTypeWidth())
	memcpyType := llvm.FunctionType(
		llvm.VoidType(),
		[]llvm.Type{
			llvm.PointerType(llvm.Int8Type(), 0),
			llvm.PointerType(llvm.Int8Type(), 0),
			tm.target.IntPtrType(),
			llvm.Int32Type(),
			llvm.Int1Type(),
		},
		false,
	)
	ri.memcpy = llvm.AddFunction(module, memcpyName, memcpyType)

	returnaddressType := llvm.FunctionType(
		llvm.PointerType(llvm.Int8Type(), 0),
		[]llvm.Type{llvm.Int32Type()},
		false,
	)
	ri.returnaddress = llvm.AddFunction(module, "llvm.returnaddress", returnaddressType)

	gccgoPersonalityType := llvm.FunctionType(
		llvm.Int32Type(),
		[]llvm.Type{
			llvm.Int32Type(),
			llvm.Int64Type(),
			llvm.PointerType(llvm.Int8Type(), 0),
			llvm.PointerType(llvm.Int8Type(), 0),
		},
		false,
	)
	ri.gccgoPersonality = llvm.AddFunction(module, "__gccgo_personality_v0", gccgoPersonalityType)

	ri.gccgoExceptionType = llvm.StructType(
		[]llvm.Type{
			llvm.PointerType(llvm.Int8Type(), 0),
			llvm.Int32Type(),
		},
		false,
	)

	return &ri, nil
}
Beispiel #8
0
// createThunk creates a thunk from a
// given function and arguments, suitable for use with
// "defer" and "go".
func (fr *frame) createThunk(call ssa.CallInstruction) (thunk llvm.Value, arg llvm.Value) {
	seenarg := make(map[ssa.Value]bool)
	var args []ssa.Value
	var argtypes []*types.Var

	packArg := func(arg ssa.Value) {
		switch arg.(type) {
		case *ssa.Builtin, *ssa.Function, *ssa.Const, *ssa.Global:
			// Do nothing: we can generate these in the thunk
		default:
			if !seenarg[arg] {
				seenarg[arg] = true
				args = append(args, arg)
				field := types.NewField(0, nil, "_", arg.Type(), true)
				argtypes = append(argtypes, field)
			}
		}
	}

	packArg(call.Common().Value)
	for _, arg := range call.Common().Args {
		packArg(arg)
	}

	var isRecoverCall bool
	i8ptr := llvm.PointerType(llvm.Int8Type(), 0)
	var structllptr llvm.Type
	if len(args) == 0 {
		if builtin, ok := call.Common().Value.(*ssa.Builtin); ok {
			isRecoverCall = builtin.Name() == "recover"
		}
		if isRecoverCall {
			// When creating a thunk for recover(), we must pass fr.canRecover.
			arg = fr.builder.CreateZExt(fr.canRecover, fr.target.IntPtrType(), "")
			arg = fr.builder.CreateIntToPtr(arg, i8ptr, "")
		} else {
			arg = llvm.ConstPointerNull(i8ptr)
		}
	} else {
		structtype := types.NewStruct(argtypes, nil)
		arg = fr.createTypeMalloc(structtype)
		structllptr = arg.Type()
		for i, ssaarg := range args {
			argptr := fr.builder.CreateStructGEP(arg, i, "")
			fr.builder.CreateStore(fr.llvmvalue(ssaarg), argptr)
		}
		arg = fr.builder.CreateBitCast(arg, i8ptr, "")
	}

	thunkfntype := llvm.FunctionType(llvm.VoidType(), []llvm.Type{i8ptr}, false)
	thunkfn := llvm.AddFunction(fr.module.Module, "", thunkfntype)
	thunkfn.SetLinkage(llvm.InternalLinkage)
	fr.addCommonFunctionAttrs(thunkfn)

	thunkfr := newFrame(fr.unit, thunkfn)
	defer thunkfr.dispose()

	prologuebb := llvm.AddBasicBlock(thunkfn, "prologue")
	thunkfr.builder.SetInsertPointAtEnd(prologuebb)

	if isRecoverCall {
		thunkarg := thunkfn.Param(0)
		thunkarg = thunkfr.builder.CreatePtrToInt(thunkarg, fr.target.IntPtrType(), "")
		thunkfr.canRecover = thunkfr.builder.CreateTrunc(thunkarg, llvm.Int1Type(), "")
	} else if len(args) > 0 {
		thunkarg := thunkfn.Param(0)
		thunkarg = thunkfr.builder.CreateBitCast(thunkarg, structllptr, "")
		for i, ssaarg := range args {
			thunkargptr := thunkfr.builder.CreateStructGEP(thunkarg, i, "")
			thunkarg := thunkfr.builder.CreateLoad(thunkargptr, "")
			thunkfr.env[ssaarg] = newValue(thunkarg, ssaarg.Type())
		}
	}

	_, isDefer := call.(*ssa.Defer)

	entrybb := llvm.AddBasicBlock(thunkfn, "entry")
	br := thunkfr.builder.CreateBr(entrybb)
	thunkfr.allocaBuilder.SetInsertPointBefore(br)

	thunkfr.builder.SetInsertPointAtEnd(entrybb)
	var exitbb llvm.BasicBlock
	if isDefer {
		exitbb = llvm.AddBasicBlock(thunkfn, "exit")
		thunkfr.runtime.setDeferRetaddr.call(thunkfr, llvm.BlockAddress(thunkfn, exitbb))
	}
	if isDefer && isRecoverCall {
		thunkfr.callRecover(true)
	} else {
		thunkfr.callInstruction(call)
	}
	if isDefer {
		thunkfr.builder.CreateBr(exitbb)
		thunkfr.builder.SetInsertPointAtEnd(exitbb)
	}
	thunkfr.builder.CreateRetVoid()

	thunk = fr.builder.CreateBitCast(thunkfn, i8ptr, "")
	return
}
Beispiel #9
0
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))
	}
}
Beispiel #10
0
// mapIterNext advances the iterator, and returns the tuple (ok, k, v).
func (fr *frame) mapIterNext(iter []*govalue) []*govalue {
	maptyp := iter[0].Type().Underlying().(*types.Map)
	ktyp := maptyp.Key()
	klltyp := fr.types.ToLLVM(ktyp)
	vtyp := maptyp.Elem()
	vlltyp := fr.types.ToLLVM(vtyp)

	m, isinitptr := iter[0], iter[1]

	i8ptr := llvm.PointerType(llvm.Int8Type(), 0)
	mapiterbufty := llvm.ArrayType(i8ptr, 4)
	mapiterbuf := fr.allocaBuilder.CreateAlloca(mapiterbufty, "")
	mapiterbufelem0ptr := fr.builder.CreateStructGEP(mapiterbuf, 0, "")

	keybuf := fr.allocaBuilder.CreateAlloca(klltyp, "")
	keyptr := fr.builder.CreateBitCast(keybuf, i8ptr, "")
	valbuf := fr.allocaBuilder.CreateAlloca(vlltyp, "")
	valptr := fr.builder.CreateBitCast(valbuf, i8ptr, "")

	isinit := fr.builder.CreateLoad(isinitptr.value, "")

	initbb := llvm.AddBasicBlock(fr.function, "")
	nextbb := llvm.AddBasicBlock(fr.function, "")
	contbb := llvm.AddBasicBlock(fr.function, "")

	fr.builder.CreateCondBr(isinit, nextbb, initbb)

	fr.builder.SetInsertPointAtEnd(initbb)
	fr.builder.CreateStore(llvm.ConstAllOnes(llvm.Int1Type()), isinitptr.value)
	fr.runtime.mapiterinit.call(fr, m.value, mapiterbufelem0ptr)
	fr.builder.CreateBr(contbb)

	fr.builder.SetInsertPointAtEnd(nextbb)
	fr.runtime.mapiternext.call(fr, mapiterbufelem0ptr)
	fr.builder.CreateBr(contbb)

	fr.builder.SetInsertPointAtEnd(contbb)
	mapiterbufelem0 := fr.builder.CreateLoad(mapiterbufelem0ptr, "")
	okbit := fr.builder.CreateIsNotNull(mapiterbufelem0, "")
	ok := fr.builder.CreateZExt(okbit, llvm.Int8Type(), "")

	loadbb := llvm.AddBasicBlock(fr.function, "")
	cont2bb := llvm.AddBasicBlock(fr.function, "")
	fr.builder.CreateCondBr(okbit, loadbb, cont2bb)

	fr.builder.SetInsertPointAtEnd(loadbb)
	fr.runtime.mapiter2.call(fr, mapiterbufelem0ptr, keyptr, valptr)
	loadbb = fr.builder.GetInsertBlock()
	loadedkey := fr.builder.CreateLoad(keybuf, "")
	loadedval := fr.builder.CreateLoad(valbuf, "")
	fr.builder.CreateBr(cont2bb)

	fr.builder.SetInsertPointAtEnd(cont2bb)
	k := fr.builder.CreatePHI(klltyp, "")
	k.AddIncoming(
		[]llvm.Value{llvm.ConstNull(klltyp), loadedkey},
		[]llvm.BasicBlock{contbb, loadbb},
	)
	v := fr.builder.CreatePHI(vlltyp, "")
	v.AddIncoming(
		[]llvm.Value{llvm.ConstNull(vlltyp), loadedval},
		[]llvm.BasicBlock{contbb, loadbb},
	)

	return []*govalue{newValue(ok, types.Typ[types.Bool]), newValue(k, ktyp), newValue(v, vtyp)}
}
Beispiel #11
0
package codegen

import (
	"log"

	"github.com/furryfaust/lyca/src/parser"
	"llvm.org/llvm/bindings/go/llvm"
)

var PRIMITIVE_TYPES = map[string]llvm.Type{
	"int": llvm.Int32Type(), "char": llvm.Int8Type(), "float": llvm.FloatType(), "boolean": llvm.Int1Type(),
}

var null llvm.Value = llvm.Value{}

func (c *Codegen) getLLVMFuncType(ret parser.Node, params []*parser.VarDeclNode, obj llvm.Type) llvm.Type {
	p := make([]llvm.Type, 0)
	if obj != llvm.VoidType() {
		p = append(p, obj)
	}

	for _, v := range params {
		p = append(p, c.getLLVMType(v.Type))
	}

	return llvm.FunctionType(c.getLLVMType(ret), p, false)
}

func (c *Codegen) getLLVMType(node parser.Node) llvm.Type {
	switch t := node.(type) {
	/*