blob: 3731ff1880c2ac00159295c78baf3e4a46ef4f7a [file] [log] [blame]
//===- 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}, "")
}