696 lines
20 KiB
Go
696 lines
20 KiB
Go
|
//===- cabi.go - C ABI abstraction layer ----------------------------------===//
|
||
|
//
|
||
|
// 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 an abstraction layer for the platform's C ABI (currently
|
||
|
// supports only Linux/x86_64).
|
||
|
//
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
package irgen
|
||
|
|
||
|
import (
|
||
|
"llvm.org/llgo/third_party/gotools/go/types"
|
||
|
"llvm.org/llvm/bindings/go/llvm"
|
||
|
)
|
||
|
|
||
|
type abiArgInfo int
|
||
|
|
||
|
const (
|
||
|
AIK_Direct = abiArgInfo(iota)
|
||
|
AIK_Indirect
|
||
|
)
|
||
|
|
||
|
type backendType interface {
|
||
|
ToLLVM(llvm.Context) llvm.Type
|
||
|
}
|
||
|
|
||
|
type ptrBType struct {
|
||
|
}
|
||
|
|
||
|
func (t ptrBType) ToLLVM(c llvm.Context) llvm.Type {
|
||
|
return llvm.PointerType(c.Int8Type(), 0)
|
||
|
}
|
||
|
|
||
|
type intBType struct {
|
||
|
width int
|
||
|
signed bool
|
||
|
}
|
||
|
|
||
|
func (t intBType) ToLLVM(c llvm.Context) llvm.Type {
|
||
|
return c.IntType(t.width * 8)
|
||
|
}
|
||
|
|
||
|
type floatBType struct {
|
||
|
isDouble bool
|
||
|
}
|
||
|
|
||
|
func (t floatBType) ToLLVM(c llvm.Context) llvm.Type {
|
||
|
if t.isDouble {
|
||
|
return c.DoubleType()
|
||
|
} else {
|
||
|
return c.FloatType()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type structBType struct {
|
||
|
fields []backendType
|
||
|
}
|
||
|
|
||
|
func (t structBType) ToLLVM(c llvm.Context) llvm.Type {
|
||
|
var lfields []llvm.Type
|
||
|
for _, f := range t.fields {
|
||
|
lfields = append(lfields, f.ToLLVM(c))
|
||
|
}
|
||
|
return c.StructType(lfields, false)
|
||
|
}
|
||
|
|
||
|
type arrayBType struct {
|
||
|
length uint64
|
||
|
elem backendType
|
||
|
}
|
||
|
|
||
|
func (t arrayBType) ToLLVM(c llvm.Context) llvm.Type {
|
||
|
return llvm.ArrayType(t.elem.ToLLVM(c), int(t.length))
|
||
|
}
|
||
|
|
||
|
// align returns the smallest y >= x such that y % a == 0.
|
||
|
func align(x, a int64) int64 {
|
||
|
y := x + a - 1
|
||
|
return y - y%a
|
||
|
}
|
||
|
|
||
|
func (tm *llvmTypeMap) sizeofStruct(fields ...types.Type) int64 {
|
||
|
var o int64
|
||
|
for _, f := range fields {
|
||
|
a := tm.Alignof(f)
|
||
|
o = align(o, a)
|
||
|
o += tm.Sizeof(f)
|
||
|
}
|
||
|
return o
|
||
|
}
|
||
|
|
||
|
// This decides whether the x86_64 classification algorithm produces MEMORY for
|
||
|
// the given type. Given the subset of types that Go supports, this is exactly
|
||
|
// equivalent to testing the type's size. See in particular the first step of
|
||
|
// the algorithm and its footnote.
|
||
|
func (tm *llvmTypeMap) classify(t ...types.Type) abiArgInfo {
|
||
|
if tm.sizeofStruct(t...) > 16 {
|
||
|
return AIK_Indirect
|
||
|
}
|
||
|
return AIK_Direct
|
||
|
}
|
||
|
|
||
|
func (tm *llvmTypeMap) sliceBackendType() backendType {
|
||
|
i8ptr := &ptrBType{}
|
||
|
uintptr := &intBType{tm.target.PointerSize(), false}
|
||
|
return &structBType{[]backendType{i8ptr, uintptr, uintptr}}
|
||
|
}
|
||
|
|
||
|
func (tm *llvmTypeMap) getBackendType(t types.Type) backendType {
|
||
|
switch t := t.(type) {
|
||
|
case *types.Named:
|
||
|
return tm.getBackendType(t.Underlying())
|
||
|
|
||
|
case *types.Basic:
|
||
|
switch t.Kind() {
|
||
|
case types.Bool, types.Uint8:
|
||
|
return &intBType{1, false}
|
||
|
case types.Int8:
|
||
|
return &intBType{1, true}
|
||
|
case types.Uint16:
|
||
|
return &intBType{2, false}
|
||
|
case types.Int16:
|
||
|
return &intBType{2, true}
|
||
|
case types.Uint32:
|
||
|
return &intBType{4, false}
|
||
|
case types.Int32:
|
||
|
return &intBType{4, true}
|
||
|
case types.Uint64:
|
||
|
return &intBType{8, false}
|
||
|
case types.Int64:
|
||
|
return &intBType{8, true}
|
||
|
case types.Uint, types.Uintptr:
|
||
|
return &intBType{tm.target.PointerSize(), false}
|
||
|
case types.Int:
|
||
|
return &intBType{tm.target.PointerSize(), true}
|
||
|
case types.Float32:
|
||
|
return &floatBType{false}
|
||
|
case types.Float64:
|
||
|
return &floatBType{true}
|
||
|
case types.UnsafePointer:
|
||
|
return &ptrBType{}
|
||
|
case types.Complex64:
|
||
|
f32 := &floatBType{false}
|
||
|
return &structBType{[]backendType{f32, f32}}
|
||
|
case types.Complex128:
|
||
|
f64 := &floatBType{true}
|
||
|
return &structBType{[]backendType{f64, f64}}
|
||
|
case types.String:
|
||
|
return &structBType{[]backendType{&ptrBType{}, &intBType{tm.target.PointerSize(), false}}}
|
||
|
}
|
||
|
|
||
|
case *types.Struct:
|
||
|
var fields []backendType
|
||
|
for i := 0; i != t.NumFields(); i++ {
|
||
|
f := t.Field(i)
|
||
|
fields = append(fields, tm.getBackendType(f.Type()))
|
||
|
}
|
||
|
return &structBType{fields}
|
||
|
|
||
|
case *types.Pointer, *types.Signature, *types.Map, *types.Chan:
|
||
|
return &ptrBType{}
|
||
|
|
||
|
case *types.Interface:
|
||
|
i8ptr := &ptrBType{}
|
||
|
return &structBType{[]backendType{i8ptr, i8ptr}}
|
||
|
|
||
|
case *types.Slice:
|
||
|
return tm.sliceBackendType()
|
||
|
|
||
|
case *types.Array:
|
||
|
return &arrayBType{uint64(t.Len()), tm.getBackendType(t.Elem())}
|
||
|
}
|
||
|
|
||
|
panic("unhandled type: " + t.String())
|
||
|
}
|
||
|
|
||
|
type offsetedType struct {
|
||
|
typ backendType
|
||
|
offset uint64
|
||
|
}
|
||
|
|
||
|
func (tm *llvmTypeMap) getBackendOffsets(bt backendType) (offsets []offsetedType) {
|
||
|
switch bt := bt.(type) {
|
||
|
case *structBType:
|
||
|
t := bt.ToLLVM(tm.ctx)
|
||
|
for i, f := range bt.fields {
|
||
|
offset := tm.target.ElementOffset(t, i)
|
||
|
fieldOffsets := tm.getBackendOffsets(f)
|
||
|
for _, fo := range fieldOffsets {
|
||
|
offsets = append(offsets, offsetedType{fo.typ, offset + fo.offset})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
case *arrayBType:
|
||
|
size := tm.target.TypeAllocSize(bt.elem.ToLLVM(tm.ctx))
|
||
|
fieldOffsets := tm.getBackendOffsets(bt.elem)
|
||
|
for i := uint64(0); i != bt.length; i++ {
|
||
|
for _, fo := range fieldOffsets {
|
||
|
offsets = append(offsets, offsetedType{fo.typ, i*size + fo.offset})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
offsets = []offsetedType{offsetedType{bt, 0}}
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (tm *llvmTypeMap) classifyEightbyte(offsets []offsetedType, numInt, numSSE *int) llvm.Type {
|
||
|
if len(offsets) == 1 {
|
||
|
if _, ok := offsets[0].typ.(*floatBType); ok {
|
||
|
*numSSE++
|
||
|
} else {
|
||
|
*numInt++
|
||
|
}
|
||
|
return offsets[0].typ.ToLLVM(tm.ctx)
|
||
|
}
|
||
|
// This implements classification for the basic types and step 4 of the
|
||
|
// classification algorithm. At this point, the only two possible
|
||
|
// classifications are SSE (floats) and INTEGER (everything else).
|
||
|
sse := true
|
||
|
for _, ot := range offsets {
|
||
|
if _, ok := ot.typ.(*floatBType); !ok {
|
||
|
sse = false
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
if sse {
|
||
|
// This can only be (float, float), which uses an SSE vector.
|
||
|
*numSSE++
|
||
|
return llvm.VectorType(tm.ctx.FloatType(), 2)
|
||
|
} else {
|
||
|
*numInt++
|
||
|
width := offsets[len(offsets)-1].offset + tm.target.TypeAllocSize(offsets[len(offsets)-1].typ.ToLLVM(tm.ctx)) - offsets[0].offset
|
||
|
return tm.ctx.IntType(int(width) * 8)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (tm *llvmTypeMap) expandType(argTypes []llvm.Type, argAttrs []llvm.Attribute, bt backendType) ([]llvm.Type, []llvm.Attribute, int, int) {
|
||
|
var numInt, numSSE int
|
||
|
var argAttr llvm.Attribute
|
||
|
|
||
|
switch bt := bt.(type) {
|
||
|
case *structBType, *arrayBType:
|
||
|
noneAttr := tm.ctx.CreateEnumAttribute(0, 0)
|
||
|
bo := tm.getBackendOffsets(bt)
|
||
|
sp := 0
|
||
|
for sp != len(bo) && bo[sp].offset < 8 {
|
||
|
sp++
|
||
|
}
|
||
|
eb1 := bo[0:sp]
|
||
|
eb2 := bo[sp:]
|
||
|
if len(eb2) > 0 {
|
||
|
argTypes = append(argTypes, tm.classifyEightbyte(eb1, &numInt, &numSSE), tm.classifyEightbyte(eb2, &numInt, &numSSE))
|
||
|
argAttrs = append(argAttrs, noneAttr, noneAttr)
|
||
|
} else {
|
||
|
argTypes = append(argTypes, tm.classifyEightbyte(eb1, &numInt, &numSSE))
|
||
|
argAttrs = append(argAttrs, noneAttr)
|
||
|
}
|
||
|
|
||
|
return argTypes, argAttrs, numInt, numSSE
|
||
|
|
||
|
case *intBType:
|
||
|
if bt.width < 4 {
|
||
|
var argAttrKind uint
|
||
|
if bt.signed {
|
||
|
argAttrKind = llvm.AttributeKindID("signext")
|
||
|
} else {
|
||
|
argAttrKind = llvm.AttributeKindID("zeroext")
|
||
|
}
|
||
|
argAttr = tm.ctx.CreateEnumAttribute(argAttrKind, 0)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
argTypes = append(argTypes, tm.classifyEightbyte([]offsetedType{{bt, 0}}, &numInt, &numSSE))
|
||
|
argAttrs = append(argAttrs, argAttr)
|
||
|
|
||
|
return argTypes, argAttrs, numInt, numSSE
|
||
|
}
|
||
|
|
||
|
type argInfo interface {
|
||
|
// Emit instructions to builder to ABI encode val and store result to args.
|
||
|
encode(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder, args []llvm.Value, val llvm.Value)
|
||
|
|
||
|
// Emit instructions to builder to ABI decode and return the resulting Value.
|
||
|
decode(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder) llvm.Value
|
||
|
}
|
||
|
|
||
|
type retInfo interface {
|
||
|
// Prepare args to receive a value. allocaBuilder refers to a builder in the entry block.
|
||
|
prepare(ctx llvm.Context, allocaBuilder llvm.Builder, args []llvm.Value)
|
||
|
|
||
|
// Emit instructions to builder to ABI decode the return value(s), if any. call is the
|
||
|
// call instruction. Must be called after prepare().
|
||
|
decode(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder, call llvm.Value) []llvm.Value
|
||
|
|
||
|
// Emit instructions to builder to ABI encode the return value(s), if any, and return.
|
||
|
encode(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder, vals []llvm.Value)
|
||
|
}
|
||
|
|
||
|
type directArgInfo struct {
|
||
|
argOffset int
|
||
|
argTypes []llvm.Type
|
||
|
valType llvm.Type
|
||
|
}
|
||
|
|
||
|
func directEncode(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder, argTypes []llvm.Type, args []llvm.Value, val llvm.Value) {
|
||
|
valType := val.Type()
|
||
|
|
||
|
switch len(argTypes) {
|
||
|
case 0:
|
||
|
// do nothing
|
||
|
|
||
|
case 1:
|
||
|
if argTypes[0].C == valType.C {
|
||
|
args[0] = val
|
||
|
return
|
||
|
}
|
||
|
alloca := allocaBuilder.CreateAlloca(valType, "")
|
||
|
bitcast := builder.CreateBitCast(alloca, llvm.PointerType(argTypes[0], 0), "")
|
||
|
builder.CreateStore(val, alloca)
|
||
|
args[0] = builder.CreateLoad(bitcast, "")
|
||
|
|
||
|
case 2:
|
||
|
encodeType := llvm.StructType(argTypes, false)
|
||
|
alloca := allocaBuilder.CreateAlloca(valType, "")
|
||
|
bitcast := builder.CreateBitCast(alloca, llvm.PointerType(encodeType, 0), "")
|
||
|
builder.CreateStore(val, alloca)
|
||
|
args[0] = builder.CreateLoad(builder.CreateStructGEP(bitcast, 0, ""), "")
|
||
|
args[1] = builder.CreateLoad(builder.CreateStructGEP(bitcast, 1, ""), "")
|
||
|
|
||
|
default:
|
||
|
panic("unexpected argTypes size")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (ai *directArgInfo) encode(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder, args []llvm.Value, val llvm.Value) {
|
||
|
directEncode(ctx, allocaBuilder, builder, ai.argTypes, args[ai.argOffset:ai.argOffset+len(ai.argTypes)], val)
|
||
|
}
|
||
|
|
||
|
func directDecode(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder, valType llvm.Type, args []llvm.Value) llvm.Value {
|
||
|
var alloca llvm.Value
|
||
|
|
||
|
switch len(args) {
|
||
|
case 0:
|
||
|
return llvm.ConstNull(ctx.StructType(nil, false))
|
||
|
|
||
|
case 1:
|
||
|
if args[0].Type().C == valType.C {
|
||
|
return args[0]
|
||
|
}
|
||
|
alloca = allocaBuilder.CreateAlloca(valType, "")
|
||
|
bitcast := builder.CreateBitCast(alloca, llvm.PointerType(args[0].Type(), 0), "")
|
||
|
builder.CreateStore(args[0], bitcast)
|
||
|
|
||
|
case 2:
|
||
|
alloca = allocaBuilder.CreateAlloca(valType, "")
|
||
|
var argTypes []llvm.Type
|
||
|
for _, a := range args {
|
||
|
argTypes = append(argTypes, a.Type())
|
||
|
}
|
||
|
encodeType := ctx.StructType(argTypes, false)
|
||
|
bitcast := builder.CreateBitCast(alloca, llvm.PointerType(encodeType, 0), "")
|
||
|
builder.CreateStore(args[0], builder.CreateStructGEP(bitcast, 0, ""))
|
||
|
builder.CreateStore(args[1], builder.CreateStructGEP(bitcast, 1, ""))
|
||
|
|
||
|
default:
|
||
|
panic("unexpected argTypes size")
|
||
|
}
|
||
|
|
||
|
return builder.CreateLoad(alloca, "")
|
||
|
}
|
||
|
|
||
|
func (ai *directArgInfo) decode(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder) llvm.Value {
|
||
|
var args []llvm.Value
|
||
|
fn := builder.GetInsertBlock().Parent()
|
||
|
for i, _ := range ai.argTypes {
|
||
|
args = append(args, fn.Param(ai.argOffset+i))
|
||
|
}
|
||
|
return directDecode(ctx, allocaBuilder, builder, ai.valType, args)
|
||
|
}
|
||
|
|
||
|
type indirectArgInfo struct {
|
||
|
argOffset int
|
||
|
}
|
||
|
|
||
|
func (ai *indirectArgInfo) encode(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder, args []llvm.Value, val llvm.Value) {
|
||
|
alloca := allocaBuilder.CreateAlloca(val.Type(), "")
|
||
|
builder.CreateStore(val, alloca)
|
||
|
args[ai.argOffset] = alloca
|
||
|
}
|
||
|
|
||
|
func (ai *indirectArgInfo) decode(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder) llvm.Value {
|
||
|
fn := builder.GetInsertBlock().Parent()
|
||
|
return builder.CreateLoad(fn.Param(ai.argOffset), "")
|
||
|
}
|
||
|
|
||
|
type directRetInfo struct {
|
||
|
numResults int
|
||
|
retTypes []llvm.Type
|
||
|
resultsType llvm.Type
|
||
|
}
|
||
|
|
||
|
func (ri *directRetInfo) prepare(ctx llvm.Context, allocaBuilder llvm.Builder, args []llvm.Value) {
|
||
|
}
|
||
|
|
||
|
func (ri *directRetInfo) decode(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder, call llvm.Value) []llvm.Value {
|
||
|
var args []llvm.Value
|
||
|
switch len(ri.retTypes) {
|
||
|
case 0:
|
||
|
return nil
|
||
|
case 1:
|
||
|
args = []llvm.Value{call}
|
||
|
default:
|
||
|
args = make([]llvm.Value, len(ri.retTypes))
|
||
|
for i := 0; i != len(ri.retTypes); i++ {
|
||
|
args[i] = builder.CreateExtractValue(call, i, "")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
d := directDecode(ctx, allocaBuilder, builder, ri.resultsType, args)
|
||
|
|
||
|
if ri.numResults == 1 {
|
||
|
return []llvm.Value{d}
|
||
|
} else {
|
||
|
results := make([]llvm.Value, ri.numResults)
|
||
|
for i := 0; i != ri.numResults; i++ {
|
||
|
results[i] = builder.CreateExtractValue(d, i, "")
|
||
|
}
|
||
|
return results
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (ri *directRetInfo) encode(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder, vals []llvm.Value) {
|
||
|
if len(ri.retTypes) == 0 {
|
||
|
builder.CreateRetVoid()
|
||
|
return
|
||
|
}
|
||
|
|
||
|
var val llvm.Value
|
||
|
switch ri.numResults {
|
||
|
case 1:
|
||
|
val = vals[0]
|
||
|
default:
|
||
|
val = llvm.Undef(ri.resultsType)
|
||
|
for i, v := range vals {
|
||
|
val = builder.CreateInsertValue(val, v, i, "")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
args := make([]llvm.Value, len(ri.retTypes))
|
||
|
directEncode(ctx, allocaBuilder, builder, ri.retTypes, args, val)
|
||
|
|
||
|
var retval llvm.Value
|
||
|
switch len(ri.retTypes) {
|
||
|
case 1:
|
||
|
retval = args[0]
|
||
|
default:
|
||
|
retval = llvm.Undef(ctx.StructType(ri.retTypes, false))
|
||
|
for i, a := range args {
|
||
|
retval = builder.CreateInsertValue(retval, a, i, "")
|
||
|
}
|
||
|
}
|
||
|
builder.CreateRet(retval)
|
||
|
}
|
||
|
|
||
|
type indirectRetInfo struct {
|
||
|
numResults int
|
||
|
sretSlot llvm.Value
|
||
|
resultsType llvm.Type
|
||
|
}
|
||
|
|
||
|
func (ri *indirectRetInfo) prepare(ctx llvm.Context, allocaBuilder llvm.Builder, args []llvm.Value) {
|
||
|
ri.sretSlot = allocaBuilder.CreateAlloca(ri.resultsType, "")
|
||
|
args[0] = ri.sretSlot
|
||
|
}
|
||
|
|
||
|
func (ri *indirectRetInfo) decode(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder, call llvm.Value) []llvm.Value {
|
||
|
if ri.numResults == 1 {
|
||
|
return []llvm.Value{builder.CreateLoad(ri.sretSlot, "")}
|
||
|
} else {
|
||
|
vals := make([]llvm.Value, ri.numResults)
|
||
|
for i, _ := range vals {
|
||
|
vals[i] = builder.CreateLoad(builder.CreateStructGEP(ri.sretSlot, i, ""), "")
|
||
|
}
|
||
|
return vals
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (ri *indirectRetInfo) encode(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder, vals []llvm.Value) {
|
||
|
fn := builder.GetInsertBlock().Parent()
|
||
|
sretSlot := fn.Param(0)
|
||
|
|
||
|
if ri.numResults == 1 {
|
||
|
builder.CreateStore(vals[0], sretSlot)
|
||
|
} else {
|
||
|
for i, v := range vals {
|
||
|
builder.CreateStore(v, builder.CreateStructGEP(sretSlot, i, ""))
|
||
|
}
|
||
|
}
|
||
|
builder.CreateRetVoid()
|
||
|
}
|
||
|
|
||
|
type functionTypeInfo struct {
|
||
|
functionType llvm.Type
|
||
|
argAttrs []llvm.Attribute
|
||
|
retAttr llvm.Attribute
|
||
|
argInfos []argInfo
|
||
|
retInf retInfo
|
||
|
chainIndex int
|
||
|
}
|
||
|
|
||
|
func (fi *functionTypeInfo) declare(m llvm.Module, name string) llvm.Value {
|
||
|
fn := llvm.AddFunction(m, name, fi.functionType)
|
||
|
if fi.retAttr.GetEnumKind() != 0 {
|
||
|
fn.AddAttributeAtIndex(0, fi.retAttr)
|
||
|
}
|
||
|
for i, a := range fi.argAttrs {
|
||
|
if a.GetEnumKind() != 0 {
|
||
|
fn.AddAttributeAtIndex(i + 1, a)
|
||
|
}
|
||
|
}
|
||
|
return fn
|
||
|
}
|
||
|
|
||
|
func (fi *functionTypeInfo) call(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder, callee llvm.Value, chain llvm.Value, args []llvm.Value) []llvm.Value {
|
||
|
callArgs := make([]llvm.Value, len(fi.argAttrs))
|
||
|
if chain.C == nil {
|
||
|
chain = llvm.Undef(llvm.PointerType(ctx.Int8Type(), 0))
|
||
|
}
|
||
|
callArgs[fi.chainIndex] = chain
|
||
|
for i, a := range args {
|
||
|
fi.argInfos[i].encode(ctx, allocaBuilder, builder, callArgs, a)
|
||
|
}
|
||
|
fi.retInf.prepare(ctx, allocaBuilder, callArgs)
|
||
|
typedCallee := builder.CreateBitCast(callee, llvm.PointerType(fi.functionType, 0), "")
|
||
|
call := builder.CreateCall(typedCallee, callArgs, "")
|
||
|
if fi.retAttr.GetEnumKind() != 0 {
|
||
|
call.AddCallSiteAttribute(0, fi.retAttr)
|
||
|
}
|
||
|
for i, a := range fi.argAttrs {
|
||
|
if a.GetEnumKind() != 0 {
|
||
|
call.AddCallSiteAttribute(i + 1, a)
|
||
|
}
|
||
|
}
|
||
|
return fi.retInf.decode(ctx, allocaBuilder, builder, call)
|
||
|
}
|
||
|
|
||
|
func (fi *functionTypeInfo) invoke(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder, callee llvm.Value, chain llvm.Value, args []llvm.Value, cont, lpad llvm.BasicBlock) []llvm.Value {
|
||
|
callArgs := make([]llvm.Value, len(fi.argAttrs))
|
||
|
if chain.C == nil {
|
||
|
chain = llvm.Undef(llvm.PointerType(ctx.Int8Type(), 0))
|
||
|
}
|
||
|
callArgs[fi.chainIndex] = chain
|
||
|
for i, a := range args {
|
||
|
fi.argInfos[i].encode(ctx, allocaBuilder, builder, callArgs, a)
|
||
|
}
|
||
|
fi.retInf.prepare(ctx, allocaBuilder, callArgs)
|
||
|
typedCallee := builder.CreateBitCast(callee, llvm.PointerType(fi.functionType, 0), "")
|
||
|
call := builder.CreateInvoke(typedCallee, callArgs, cont, lpad, "")
|
||
|
if fi.retAttr.GetEnumKind() != 0 {
|
||
|
call.AddCallSiteAttribute(0, fi.retAttr)
|
||
|
}
|
||
|
for i, a := range fi.argAttrs {
|
||
|
if a.GetEnumKind() != 0 {
|
||
|
call.AddCallSiteAttribute(i + 1, a)
|
||
|
}
|
||
|
}
|
||
|
builder.SetInsertPointAtEnd(cont)
|
||
|
return fi.retInf.decode(ctx, allocaBuilder, builder, call)
|
||
|
}
|
||
|
|
||
|
func (tm *llvmTypeMap) getFunctionTypeInfo(args []types.Type, results []types.Type) (fi functionTypeInfo) {
|
||
|
var returnType llvm.Type
|
||
|
var argTypes []llvm.Type
|
||
|
var argAttrKind uint
|
||
|
if len(results) == 0 {
|
||
|
returnType = llvm.VoidType()
|
||
|
fi.retInf = &directRetInfo{}
|
||
|
} else {
|
||
|
aik := tm.classify(results...)
|
||
|
|
||
|
var resultsType llvm.Type
|
||
|
if len(results) == 1 {
|
||
|
resultsType = tm.ToLLVM(results[0])
|
||
|
} else {
|
||
|
elements := make([]llvm.Type, len(results))
|
||
|
for i := range elements {
|
||
|
elements[i] = tm.ToLLVM(results[i])
|
||
|
}
|
||
|
resultsType = tm.ctx.StructType(elements, false)
|
||
|
}
|
||
|
|
||
|
switch aik {
|
||
|
case AIK_Direct:
|
||
|
var retFields []backendType
|
||
|
for _, t := range results {
|
||
|
retFields = append(retFields, tm.getBackendType(t))
|
||
|
}
|
||
|
bt := &structBType{retFields}
|
||
|
|
||
|
retTypes, retAttrs, _, _ := tm.expandType(nil, nil, bt)
|
||
|
switch len(retTypes) {
|
||
|
case 0: // e.g., empty struct
|
||
|
returnType = llvm.VoidType()
|
||
|
case 1:
|
||
|
returnType = retTypes[0]
|
||
|
fi.retAttr = retAttrs[0]
|
||
|
case 2:
|
||
|
returnType = llvm.StructType(retTypes, false)
|
||
|
default:
|
||
|
panic("unexpected expandType result")
|
||
|
}
|
||
|
fi.retInf = &directRetInfo{numResults: len(results), retTypes: retTypes, resultsType: resultsType}
|
||
|
|
||
|
case AIK_Indirect:
|
||
|
returnType = llvm.VoidType()
|
||
|
argTypes = []llvm.Type{llvm.PointerType(resultsType, 0)}
|
||
|
argAttrKind = llvm.AttributeKindID("sret")
|
||
|
fi.argAttrs = []llvm.Attribute{tm.ctx.CreateEnumAttribute(argAttrKind, 0)}
|
||
|
fi.retInf = &indirectRetInfo{numResults: len(results), resultsType: resultsType}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Allocate an argument for the call chain.
|
||
|
fi.chainIndex = len(argTypes)
|
||
|
argTypes = append(argTypes, llvm.PointerType(tm.ctx.Int8Type(), 0))
|
||
|
argAttrKind = llvm.AttributeKindID("nest")
|
||
|
fi.argAttrs = append(fi.argAttrs, tm.ctx.CreateEnumAttribute(argAttrKind, 0))
|
||
|
|
||
|
// Keep track of the number of INTEGER/SSE class registers remaining.
|
||
|
remainingInt := 6
|
||
|
remainingSSE := 8
|
||
|
|
||
|
for _, arg := range args {
|
||
|
aik := tm.classify(arg)
|
||
|
|
||
|
isDirect := aik == AIK_Direct
|
||
|
if isDirect {
|
||
|
bt := tm.getBackendType(arg)
|
||
|
directArgTypes, directArgAttrs, numInt, numSSE := tm.expandType(argTypes, fi.argAttrs, bt)
|
||
|
|
||
|
// Check if the argument can fit into the remaining registers, or if
|
||
|
// it would just occupy one register (which pushes the whole argument
|
||
|
// onto the stack anyway).
|
||
|
if numInt <= remainingInt && numSSE <= remainingSSE || numInt+numSSE == 1 {
|
||
|
remainingInt -= numInt
|
||
|
remainingSSE -= numSSE
|
||
|
argInfo := &directArgInfo{argOffset: len(argTypes), valType: bt.ToLLVM(tm.ctx)}
|
||
|
fi.argInfos = append(fi.argInfos, argInfo)
|
||
|
argTypes = directArgTypes
|
||
|
fi.argAttrs = directArgAttrs
|
||
|
argInfo.argTypes = argTypes[argInfo.argOffset:len(argTypes)]
|
||
|
} else {
|
||
|
// No remaining registers; pass on the stack.
|
||
|
isDirect = false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if !isDirect {
|
||
|
fi.argInfos = append(fi.argInfos, &indirectArgInfo{len(argTypes)})
|
||
|
argTypes = append(argTypes, llvm.PointerType(tm.ToLLVM(arg), 0))
|
||
|
argAttrKind = llvm.AttributeKindID("byval")
|
||
|
fi.argAttrs = append(fi.argAttrs, tm.ctx.CreateEnumAttribute(argAttrKind, 0))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fi.functionType = llvm.FunctionType(returnType, argTypes, false)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (tm *llvmTypeMap) getSignatureInfo(sig *types.Signature) functionTypeInfo {
|
||
|
var args, results []types.Type
|
||
|
if sig.Recv() != nil {
|
||
|
recvtype := sig.Recv().Type()
|
||
|
if _, ok := recvtype.Underlying().(*types.Pointer); !ok && recvtype != types.Typ[types.UnsafePointer] {
|
||
|
recvtype = types.NewPointer(recvtype)
|
||
|
}
|
||
|
args = []types.Type{recvtype}
|
||
|
}
|
||
|
|
||
|
for i := 0; i != sig.Params().Len(); i++ {
|
||
|
args = append(args, sig.Params().At(i).Type())
|
||
|
}
|
||
|
for i := 0; i != sig.Results().Len(); i++ {
|
||
|
results = append(results, sig.Results().At(i).Type())
|
||
|
}
|
||
|
return tm.getFunctionTypeInfo(args, results)
|
||
|
}
|