forked from OSchip/llvm-project
608 lines
15 KiB
Go
608 lines
15 KiB
Go
//===- runtime.go - IR generation for runtime calls -----------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements IR generation for calls to the runtime library.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
package irgen
|
|
|
|
import (
|
|
"strconv"
|
|
|
|
"llvm.org/llgo/third_party/gotools/go/types"
|
|
|
|
"llvm.org/llvm/bindings/go/llvm"
|
|
)
|
|
|
|
type runtimeFnInfo struct {
|
|
fi *functionTypeInfo
|
|
fn llvm.Value
|
|
}
|
|
|
|
func (rfi *runtimeFnInfo) init(tm *llvmTypeMap, m llvm.Module, name string, args []types.Type, results []types.Type) {
|
|
rfi.fi = new(functionTypeInfo)
|
|
*rfi.fi = tm.getFunctionTypeInfo(args, results)
|
|
rfi.fn = rfi.fi.declare(m, name)
|
|
}
|
|
|
|
func (rfi *runtimeFnInfo) call(f *frame, args ...llvm.Value) []llvm.Value {
|
|
if f.unwindBlock.IsNil() {
|
|
return rfi.callOnly(f, args...)
|
|
} else {
|
|
return rfi.invoke(f, f.unwindBlock, args...)
|
|
}
|
|
}
|
|
|
|
func (rfi *runtimeFnInfo) callOnly(f *frame, args ...llvm.Value) []llvm.Value {
|
|
return rfi.fi.call(f.llvmtypes.ctx, f.allocaBuilder, f.builder, rfi.fn, llvm.Value{nil}, args)
|
|
}
|
|
|
|
func (rfi *runtimeFnInfo) invoke(f *frame, lpad llvm.BasicBlock, args ...llvm.Value) []llvm.Value {
|
|
contbb := llvm.AddBasicBlock(f.function, "")
|
|
return rfi.fi.invoke(f.llvmtypes.ctx, f.allocaBuilder, f.builder, rfi.fn, llvm.Value{nil}, args, contbb, lpad)
|
|
}
|
|
|
|
// runtimeInterface is a struct containing references to
|
|
// runtime types and intrinsic function declarations.
|
|
type runtimeInterface struct {
|
|
// LLVM intrinsics
|
|
memcpy,
|
|
memset,
|
|
returnaddress llvm.Value
|
|
|
|
// Exception handling support
|
|
gccgoPersonality llvm.Value
|
|
gccgoExceptionType llvm.Type
|
|
|
|
// Runtime intrinsics
|
|
append,
|
|
assertInterface,
|
|
byteArrayToString,
|
|
canRecover,
|
|
chanCap,
|
|
chanLen,
|
|
chanrecv2,
|
|
checkDefer,
|
|
checkInterfaceType,
|
|
builtinClose,
|
|
convertInterface,
|
|
copy,
|
|
Defer,
|
|
deferredRecover,
|
|
emptyInterfaceCompare,
|
|
Go,
|
|
ifaceE2I2,
|
|
ifaceI2I2,
|
|
intArrayToString,
|
|
interfaceCompare,
|
|
intToString,
|
|
makeSlice,
|
|
mapdelete,
|
|
mapiter2,
|
|
mapiterinit,
|
|
mapiternext,
|
|
mapIndex,
|
|
mapLen,
|
|
New,
|
|
newChannel,
|
|
newMap,
|
|
newSelect,
|
|
panic,
|
|
printBool,
|
|
printComplex,
|
|
printDouble,
|
|
printEmptyInterface,
|
|
printInterface,
|
|
printInt64,
|
|
printNl,
|
|
printPointer,
|
|
printSlice,
|
|
printSpace,
|
|
printString,
|
|
printUint64,
|
|
receive,
|
|
recover,
|
|
registerGcRoots,
|
|
runtimeError,
|
|
selectdefault,
|
|
selectrecv2,
|
|
selectsend,
|
|
selectgo,
|
|
sendBig,
|
|
setDeferRetaddr,
|
|
strcmp,
|
|
stringiter2,
|
|
stringPlus,
|
|
stringSlice,
|
|
stringToByteArray,
|
|
stringToIntArray,
|
|
typeDescriptorsEqual,
|
|
undefer runtimeFnInfo
|
|
}
|
|
|
|
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)
|
|
ByteSlice := types.NewSlice(types.Typ[types.Byte])
|
|
IntSlice := types.NewSlice(types.Typ[types.Int])
|
|
|
|
AttrKind := llvm.AttributeKindID("nounwind")
|
|
NoUnwindAttr := module.Context().CreateEnumAttribute(AttrKind, 0)
|
|
AttrKind = llvm.AttributeKindID("noreturn")
|
|
NoReturnAttr := module.Context().CreateEnumAttribute(AttrKind, 0)
|
|
|
|
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_byte_array_to_string",
|
|
rfi: &ri.byteArrayToString,
|
|
args: []types.Type{UnsafePointer, Int},
|
|
res: []types.Type{String},
|
|
attrs: []llvm.Attribute{NoUnwindAttr},
|
|
},
|
|
{
|
|
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},
|
|
attrs: []llvm.Attribute{NoUnwindAttr},
|
|
},
|
|
{
|
|
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: "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{NoReturnAttr},
|
|
},
|
|
{
|
|
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{NoReturnAttr},
|
|
},
|
|
{
|
|
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_byte_array",
|
|
rfi: &ri.stringToByteArray,
|
|
args: []types.Type{String},
|
|
res: []types.Type{ByteSlice},
|
|
attrs: []llvm.Attribute{NoUnwindAttr},
|
|
},
|
|
{
|
|
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.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),
|
|
llvm.Int64Type(),
|
|
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
|
|
}
|
|
|
|
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 (fr *frame) createTypeMalloc(t types.Type) llvm.Value {
|
|
size := llvm.ConstInt(fr.target.IntPtrType(), uint64(fr.llvmtypes.Sizeof(t)), false)
|
|
malloc := fr.runtime.New.callOnly(fr, fr.types.ToRuntime(t), size)[0]
|
|
return fr.builder.CreateBitCast(malloc, llvm.PointerType(fr.types.ToLLVM(t), 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(), "")
|
|
isvolatile := llvm.ConstNull(llvm.Int1Type())
|
|
fr.builder.CreateCall(memset, []llvm.Value{ptr, fill, size, isvolatile}, "")
|
|
}
|
|
|
|
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(), "")
|
|
isvolatile := llvm.ConstNull(llvm.Int1Type())
|
|
fr.builder.CreateCall(memcpy, []llvm.Value{dest, src, size, isvolatile}, "")
|
|
}
|
|
|
|
func (fr *frame) returnAddress(level uint64) llvm.Value {
|
|
returnaddress := fr.runtime.returnaddress
|
|
levelValue := llvm.ConstInt(llvm.Int32Type(), level, false)
|
|
return fr.builder.CreateCall(returnaddress, []llvm.Value{levelValue}, "")
|
|
}
|