internal/cabi: transform pkg to cabi

This commit is contained in:
visualfc
2025-08-12 09:30:16 +08:00
parent 2a88d5777a
commit 3ac2929789
2 changed files with 901 additions and 0 deletions

356
internal/cabi/arch.go Normal file
View File

@@ -0,0 +1,356 @@
package cabi
import (
"github.com/goplus/llvm"
)
func elementTypesCount(typ llvm.Type) int {
switch typ.TypeKind() {
case llvm.VoidTypeKind:
return 0
case llvm.StructTypeKind:
var count int
for _, t := range typ.StructElementTypes() {
count += elementTypesCount(t)
}
return count
case llvm.ArrayTypeKind:
return typ.ArrayLength() * elementTypesCount(typ.ElementType())
}
return 1
}
func elementTypes(td llvm.TargetData, typ llvm.Type) (types []llvm.Type) {
switch typ.TypeKind() {
case llvm.VoidTypeKind:
case llvm.StructTypeKind:
for _, t := range typ.StructElementTypes() {
types = append(types, elementTypes(td, t)...)
}
case llvm.ArrayTypeKind:
sub := elementTypes(td, typ.ElementType())
n := typ.ArrayLength()
for i := 0; i < n; i++ {
types = append(types, sub...)
}
default:
types = append(types, typ)
}
return
}
func checkTypes(typs []llvm.Type, typ llvm.Type) bool {
for _, t := range typs {
if t != typ {
return false
}
}
return true
}
func hasTypes(typs []llvm.Type, typ llvm.Type) bool {
for _, t := range typs {
if t == typ {
return true
}
}
return false
}
type TypeInfoAmd64 struct {
*Transformer
}
func (p *TypeInfoAmd64) SupportByVal() bool {
return true
}
func (p *TypeInfoAmd64) IsWrapType(ctx llvm.Context, typ llvm.Type, bret bool) bool {
return elementTypesCount(typ) >= 2
}
func (p *TypeInfoAmd64) GetTypeInfo(ctx llvm.Context, typ llvm.Type, bret bool) *TypeInfo {
info := &TypeInfo{}
info.Type = typ
info.Type1 = typ
if typ.TypeKind() == llvm.VoidTypeKind {
info.Kind = AttrVoid
return info
} else if typ.TypeKind() == llvm.PointerTypeKind {
}
info.Size = p.Sizeof(typ)
info.Align = p.Alignof(typ)
if n := elementTypesCount(typ); n >= 2 {
if info.Size > 16 {
info.Kind = AttrPointer
info.Type1 = llvm.PointerType(typ, 0)
} else if info.Size <= 8 {
info.Kind = AttrWidthType
info.Type1 = ctx.IntType(info.Size * 8)
types := elementTypes(p.td, typ)
if types[0] == ctx.FloatType() && types[1] == ctx.FloatType() {
info.Type1 = llvm.VectorType(ctx.FloatType(), 2)
}
} else {
types := elementTypes(p.td, typ)
if n == 2 {
// skip (float32,float32)
if types[0] == ctx.FloatType() && types[1] == ctx.FloatType() {
return info
}
// skip (i64|double,*) (*,i64/double)
if p.Sizeof(types[0]) == 8 || p.Sizeof(types[1]) == 8 {
return info
}
}
info.Kind = AttrWidthType2
var count int
for i, et := range types {
count += p.Sizeof(et)
if count >= 8 {
if i == 0 {
info.Type1 = et
} else if i == 1 && types[0] == ctx.FloatType() && types[1] == ctx.FloatType() {
info.Type1 = llvm.VectorType(ctx.FloatType(), 2)
} else {
info.Type1 = ctx.Int64Type()
}
right := len(types) - i
if count == 8 {
right--
}
if right == 1 {
info.Type2 = types[len(types)-1]
} else if right == 2 && types[len(types)-1] == ctx.FloatType() && types[len(types)-2] == ctx.FloatType() {
info.Type2 = llvm.VectorType(ctx.FloatType(), 2)
} else {
info.Type2 = ctx.IntType((info.Size - 8) * 8)
}
break
}
}
}
}
return info
}
type TypeInfoArm64 struct {
*Transformer
}
func (p *TypeInfoArm64) SupportByVal() bool {
return false
}
func (p *TypeInfoArm64) IsWrapType(ctx llvm.Context, typ llvm.Type, bret bool) bool {
switch typ.TypeKind() {
case llvm.StructTypeKind, llvm.ArrayTypeKind:
if bret && elementTypesCount(typ) == 1 {
return false
}
return true
default:
return false
}
}
func (p *TypeInfoArm64) GetTypeInfo(ctx llvm.Context, typ llvm.Type, bret bool) *TypeInfo {
info := &TypeInfo{}
info.Type = typ
info.Type1 = typ
kind := typ.TypeKind()
if kind == llvm.VoidTypeKind {
info.Kind = AttrVoid
return info
}
info.Size = p.Sizeof(typ)
info.Align = p.Alignof(typ)
switch kind {
case llvm.StructTypeKind, llvm.ArrayTypeKind:
types := elementTypes(p.td, typ)
n := len(types)
if bret && n == 1 {
return info
}
if n == 2 {
// skip (i64/ptr,i64/ptr)
if (types[0].TypeKind() == llvm.PointerTypeKind || types[0] == ctx.Int64Type()) &&
(types[1].TypeKind() == llvm.PointerTypeKind || types[1] == ctx.Int64Type()) {
return info
}
}
if n <= 4 {
if checkTypes(types, ctx.FloatType()) || checkTypes(types, ctx.DoubleType()) {
return info
}
}
if info.Size > 16 {
info.Kind = AttrPointer
info.Type1 = llvm.PointerType(typ, 0)
} else if info.Size <= 8 {
info.Kind = AttrWidthType
if bret {
info.Type1 = ctx.IntType(info.Size * 8)
} else {
info.Type1 = ctx.Int64Type()
}
} else {
info.Kind = AttrWidthType
info.Type1 = llvm.ArrayType(ctx.Int64Type(), 2)
}
}
return info
}
type TypeInfoArm struct {
*Transformer
}
func (p *TypeInfoArm) SupportByVal() bool {
return false
}
func (p *TypeInfoArm) IsWrapType(ctx llvm.Context, typ llvm.Type, bret bool) bool {
switch typ.TypeKind() {
case llvm.StructTypeKind, llvm.ArrayTypeKind:
if bret && elementTypesCount(typ) == 1 {
return false
}
return true
default:
return false
}
}
func (p *TypeInfoArm) GetTypeInfo(ctx llvm.Context, typ llvm.Type, bret bool) *TypeInfo {
info := &TypeInfo{}
info.Type = typ
info.Type1 = typ
kind := typ.TypeKind()
if kind == llvm.VoidTypeKind {
info.Kind = AttrVoid
return info
}
info.Size = p.Sizeof(typ)
info.Align = p.Alignof(typ)
switch kind {
case llvm.StructTypeKind, llvm.ArrayTypeKind:
types := elementTypes(p.td, typ)
n := len(types)
if bret && n == 1 {
return info
}
if n <= 4 {
if checkTypes(types, ctx.FloatType()) || checkTypes(types, ctx.DoubleType()) {
return info
}
}
if bret {
if info.Size > 4 {
info.Kind = AttrPointer
info.Type1 = llvm.PointerType(typ, 0)
} else {
info.Kind = AttrWidthType
info.Type1 = ctx.Int32Type()
}
} else {
if info.Size > 64 {
info.Kind = AttrPointer
info.Type1 = llvm.PointerType(typ, 0)
} else {
info.Kind = AttrWidthType
if hasTypes(types, ctx.Int64Type()) || hasTypes(types, ctx.DoubleType()) {
info.Type1 = llvm.ArrayType(ctx.Int64Type(), info.Size/8)
} else {
info.Type1 = llvm.ArrayType(ctx.Int32Type(), info.Size/4)
}
}
}
}
return info
}
type TypeInfo32 struct {
*Transformer
}
func (p *TypeInfo32) SupportByVal() bool {
return true
}
func (p *TypeInfo32) IsWrapType(ctx llvm.Context, typ llvm.Type, bret bool) bool {
return elementTypesCount(typ) >= 2
}
func (p *TypeInfo32) GetTypeInfo(ctx llvm.Context, typ llvm.Type, bret bool) *TypeInfo {
info := &TypeInfo{}
info.Type = typ
info.Type1 = typ
if typ.TypeKind() == llvm.VoidTypeKind {
info.Kind = AttrVoid
return info
}
info.Size = p.Sizeof(typ)
info.Align = p.Alignof(typ)
if n := elementTypesCount(typ); n >= 2 {
info.Kind = AttrPointer
info.Type1 = llvm.PointerType(typ, 0)
}
return info
}
type TypeInfoRiscv64 struct {
*Transformer
}
func (p *TypeInfoRiscv64) SupportByVal() bool {
return true
}
func (p *TypeInfoRiscv64) IsWrapType(ctx llvm.Context, typ llvm.Type, bret bool) bool {
switch typ.TypeKind() {
case llvm.StructTypeKind, llvm.ArrayTypeKind:
return true
}
return false
}
func (p *TypeInfoRiscv64) GetTypeInfo(ctx llvm.Context, typ llvm.Type, bret bool) *TypeInfo {
info := &TypeInfo{}
info.Type = typ
info.Type1 = typ
if typ.TypeKind() == llvm.VoidTypeKind {
info.Kind = AttrVoid
return info
}
info.Size = p.Sizeof(typ)
info.Align = p.Alignof(typ)
switch typ.TypeKind() {
case llvm.StructTypeKind, llvm.ArrayTypeKind:
types := elementTypes(p.td, typ)
switch len(types) {
case 1:
if types[0].TypeKind() == llvm.PointerTypeKind || types[0] == ctx.Int64Type() {
return info
}
case 2:
if (types[0].TypeKind() == llvm.PointerTypeKind || types[0] == ctx.Int64Type()) &&
(types[1].TypeKind() == llvm.PointerTypeKind || types[1] == ctx.Int64Type()) {
return info
}
}
if info.Size > 16 {
info.Kind = AttrPointer
info.Type1 = llvm.PointerType(typ, 0)
} else if info.Size <= 8 {
info.Kind = AttrWidthType
info.Type1 = ctx.Int64Type()
} else {
info.Kind = AttrWidthType
info.Type1 = llvm.ArrayType(ctx.Int64Type(), 2)
}
}
return info
}

545
internal/cabi/cabi.go Normal file
View File

@@ -0,0 +1,545 @@
package cabi
import (
"strings"
"github.com/goplus/llgo/ssa"
"github.com/goplus/llvm"
)
type Mode int
const (
ModeNone Mode = iota
ModeCFunc
ModeAllFunc
)
func NewTransformer(prog ssa.Program, mode Mode) *Transformer {
target := prog.Target()
tr := &Transformer{
prog: prog,
td: prog.TargetData(),
GOOS: target.GOOS,
GOARCH: target.GOARCH,
mode: mode,
}
switch target.GOARCH {
case "amd64":
tr.sys = &TypeInfoAmd64{tr}
case "arm64":
tr.sys = &TypeInfoArm64{tr}
case "arm":
tr.sys = &TypeInfoArm{tr}
case "wasm", "i386":
tr.sys = &TypeInfo32{tr}
case "riscv64":
tr.sys = &TypeInfoRiscv64{tr}
}
return tr
}
type Transformer struct {
prog ssa.Program
td llvm.TargetData
GOOS string
GOARCH string
sys TypeInfoSys
mode Mode
}
func (p *Transformer) isCFunc(name string) bool {
return !strings.Contains(name, ".")
}
func (p *Transformer) TransformModule(pkg ssa.Package) {
m := pkg.Module()
ctx := m.Context()
var fns []llvm.Value
var callInstrs []llvm.Value
switch p.mode {
case ModeNone:
return
case ModeCFunc:
fn := m.FirstFunction()
for !fn.IsNil() {
if p.isCFunc(fn.Name()) {
p.transformFuncCall(m, fn)
if p.isWrapFunctionType(m.Context(), fn.GlobalValueType()) {
fns = append(fns, fn)
}
}
fn = llvm.NextFunction(fn)
}
case ModeAllFunc:
fn := m.FirstFunction()
for !fn.IsNil() {
if p.isWrapFunctionType(ctx, fn.GlobalValueType()) {
fns = append(fns, fn)
}
bb := fn.FirstBasicBlock()
for !bb.IsNil() {
instr := bb.FirstInstruction()
for !instr.IsNil() {
if call := instr.IsACallInst(); !call.IsNil() {
if p.isWrapFunctionType(ctx, call.CalledFunctionType()) {
callInstrs = append(callInstrs, call)
}
}
instr = llvm.NextInstruction(instr)
}
bb = llvm.NextBasicBlock(bb)
}
fn = llvm.NextFunction(fn)
}
}
for _, call := range callInstrs {
p.transformCallInstr(ctx, call)
}
for _, fn := range fns {
p.transformFunc(m, fn)
}
}
func (p *Transformer) isWrapFunctionType(ctx llvm.Context, ft llvm.Type) bool {
if p.IsWrapType(ctx, ft.ReturnType(), true) {
return true
}
for _, typ := range ft.ParamTypes() {
if p.IsWrapType(ctx, typ, false) {
return true
}
}
return false
}
type TypeInfoSys interface {
SupportByVal() bool
IsWrapType(ctx llvm.Context, typ llvm.Type, bret bool) bool
GetTypeInfo(ctx llvm.Context, typ llvm.Type, bret bool) *TypeInfo
}
type AttrKind int
const (
AttrNone AttrKind = iota
AttrVoid // param type void
AttrPointer // param type => type*
AttrWidthType // type => width int i16/i24/i32/i40/i48/i56/i64 float/double
AttrWidthType2 // type => width two int {i64,i16} float/double
)
type FuncInfo struct {
Type llvm.Type // func type
Return *TypeInfo // return info
Params []*TypeInfo // params info
}
func (p *FuncInfo) HasWrap() bool {
if p.Return.Kind > AttrVoid {
return true
}
for _, t := range p.Params {
if t.Kind > AttrVoid {
return true
}
}
return false
}
type TypeInfo struct {
Type llvm.Type
Kind AttrKind
Type1 llvm.Type // AttrWidthType
Type2 llvm.Type // AttrWidthType2
Size int
Align int
}
func byvalAttribute(ctx llvm.Context, typ llvm.Type) llvm.Attribute {
id := llvm.AttributeKindID("byval")
return ctx.CreateTypeAttribute(id, typ)
}
func sretAttribute(ctx llvm.Context, typ llvm.Type) llvm.Attribute {
id := llvm.AttributeKindID("sret")
return ctx.CreateTypeAttribute(id, typ)
}
func funcInlineHint(ctx llvm.Context) llvm.Attribute {
return ctx.CreateEnumAttribute(llvm.AttributeKindID("inlinehint"), 0)
}
func (p *Transformer) IsWrapType(ctx llvm.Context, typ llvm.Type, bret bool) bool {
if p.sys != nil {
return p.sys.IsWrapType(ctx, typ, bret)
}
return false
}
func (p *Transformer) GetTypeInfo(ctx llvm.Context, typ llvm.Type, bret bool) *TypeInfo {
if p.sys != nil {
return p.sys.GetTypeInfo(ctx, typ, bret)
}
panic("not implment: " + p.GOARCH)
}
func (p *Transformer) Sizeof(typ llvm.Type) int {
return int(p.td.TypeAllocSize(typ))
}
func (p *Transformer) Alignof(typ llvm.Type) int {
return int(p.td.ABITypeAlignment(typ))
}
func (p *Transformer) GetFuncInfo(ctx llvm.Context, typ llvm.Type) (info FuncInfo) {
info.Type = typ
info.Return = p.GetTypeInfo(ctx, typ.ReturnType(), true)
params := typ.ParamTypes()
info.Params = make([]*TypeInfo, len(params))
for i, t := range params {
info.Params[i] = p.GetTypeInfo(ctx, t, false)
}
return
}
func (p *Transformer) transformFuncType(ctx llvm.Context, info *FuncInfo) (llvm.Type, map[int]llvm.Attribute) {
var paramTypes []llvm.Type
var returnType llvm.Type
attrs := make(map[int]llvm.Attribute)
switch info.Return.Kind {
case AttrPointer:
returnType = ctx.VoidType()
paramTypes = append(paramTypes, info.Return.Type1)
attrs[1] = sretAttribute(ctx, info.Return.Type)
case AttrWidthType:
returnType = info.Return.Type1
case AttrWidthType2:
returnType = llvm.StructType([]llvm.Type{info.Return.Type1, info.Return.Type2}, false)
default:
returnType = info.Return.Type1
}
for _, ti := range info.Params {
switch ti.Kind {
case AttrNone, AttrWidthType:
paramTypes = append(paramTypes, ti.Type1)
case AttrPointer:
paramTypes = append(paramTypes, ti.Type1)
if p.sys.SupportByVal() {
attrs[len(paramTypes)] = byvalAttribute(ctx, ti.Type)
}
case AttrWidthType2:
paramTypes = append(paramTypes, ti.Type1, ti.Type2)
}
}
return llvm.FunctionType(returnType, paramTypes, info.Type.IsFunctionVarArg()), attrs
}
func (p *Transformer) transformFunc(m llvm.Module, fn llvm.Value) bool {
ctx := m.Context()
info := p.GetFuncInfo(ctx, fn.GlobalValueType())
if !info.HasWrap() {
return false
}
nft, attrs := p.transformFuncType(ctx, &info)
fname := fn.Name()
fn.SetName("")
nfn := llvm.AddFunction(m, fname, nft)
for i, attr := range attrs {
nfn.AddAttributeAtIndex(i, attr)
}
nfn.SetLinkage(fn.Linkage())
nfn.SetFunctionCallConv(fn.FunctionCallConv())
for _, attr := range fn.GetFunctionAttributes() {
nfn.AddAttributeAtIndex(-1, attr)
}
if sp := fn.Subprogram(); !sp.IsNil() {
nfn.SetSubprogram(sp)
}
if !fn.IsDeclaration() {
p.transformFuncBody(ctx, &info, fn, nfn, nft)
}
fn.ReplaceAllUsesWith(nfn)
fn.EraseFromParentAsFunction()
return true
}
func (p *Transformer) transformFuncBody(ctx llvm.Context, info *FuncInfo, fn llvm.Value, nfn llvm.Value, nft llvm.Type) {
var blocks []llvm.BasicBlock
bb := fn.FirstBasicBlock()
for !bb.IsNil() {
blocks = append(blocks, bb)
bb = llvm.NextBasicBlock(bb)
}
for _, bb := range blocks {
bb.RemoveFromParent()
llvm.AppendExistingBasicBlock(nfn, bb)
}
b := ctx.NewBuilder()
b.SetInsertPointBefore(nfn.EntryBasicBlock().FirstInstruction())
params := nfn.Params()
index := 0
if info.Return.Kind == AttrPointer {
index++
}
for i, ti := range info.Params {
nv := params[index]
switch ti.Kind {
default:
case AttrPointer:
nv = b.CreateLoad(ti.Type, params[index], "")
case AttrWidthType:
iptr := llvm.CreateAlloca(b, ti.Type1)
b.CreateStore(params[index], iptr)
ptr := b.CreateBitCast(iptr, llvm.PointerType(ti.Type, 0), "")
nv = b.CreateLoad(ti.Type, ptr, "")
case AttrWidthType2:
typ := llvm.StructType([]llvm.Type{ti.Type1, ti.Type2}, false)
iptr := llvm.CreateAlloca(b, typ)
b.CreateStore(params[index], b.CreateStructGEP(typ, iptr, 0, ""))
index++
b.CreateStore(params[index], b.CreateStructGEP(typ, iptr, 1, ""))
ptr := b.CreateBitCast(iptr, llvm.PointerType(ti.Type, 0), "")
nv = b.CreateLoad(ti.Type, ptr, "")
}
fn.Param(i).ReplaceAllUsesWith(nv)
index++
}
if info.Return.Kind >= AttrPointer {
var retInstrs []llvm.Value
bb := nfn.FirstBasicBlock()
for !bb.IsNil() {
instr := bb.FirstInstruction()
for !instr.IsNil() {
if !instr.IsAReturnInst().IsNil() {
retInstrs = append(retInstrs, instr)
}
instr = llvm.NextInstruction(instr)
}
bb = llvm.NextBasicBlock(bb)
}
for _, instr := range retInstrs {
ret := instr.Operand(0)
b.SetInsertPointBefore(instr)
var rv llvm.Value
switch info.Return.Kind {
case AttrPointer:
b.CreateStore(ret, params[0])
rv = b.CreateRetVoid()
case AttrWidthType, AttrWidthType2:
ptr := llvm.CreateAlloca(b, info.Return.Type)
b.CreateStore(ret, ptr)
iptr := b.CreateBitCast(ptr, llvm.PointerType(nft.ReturnType(), 0), "")
rv = b.CreateRet(b.CreateLoad(nft.ReturnType(), iptr, ""))
}
instr.ReplaceAllUsesWith(rv)
instr.EraseFromParentAsInstruction()
}
}
}
func (p *Transformer) transformCallInstr(ctx llvm.Context, call llvm.Value) bool {
nfn := call.CalledValue()
info := p.GetFuncInfo(ctx, call.CalledFunctionType())
if !info.HasWrap() {
return false
}
nft, attrs := p.transformFuncType(ctx, &info)
b := ctx.NewBuilder()
b.SetInsertPointBefore(call)
operandCount := len(info.Params)
var nparams []llvm.Value
for i := 0; i < operandCount; i++ {
param := call.Operand(i)
ti := info.Params[i]
switch ti.Kind {
default:
nparams = append(nparams, param)
case AttrPointer:
ptr := llvm.CreateAlloca(b, ti.Type)
b.CreateStore(param, ptr)
nparams = append(nparams, ptr)
case AttrWidthType:
ptr := llvm.CreateAlloca(b, ti.Type)
b.CreateStore(param, ptr)
iptr := b.CreateBitCast(ptr, llvm.PointerType(ti.Type1, 0), "")
nparams = append(nparams, b.CreateLoad(ti.Type1, iptr, ""))
case AttrWidthType2:
ptr := llvm.CreateAlloca(b, ti.Type)
b.CreateStore(param, ptr)
typ := llvm.StructType([]llvm.Type{ti.Type1, ti.Type2}, false) // {i8,i64}
iptr := b.CreateBitCast(ptr, llvm.PointerType(typ, 0), "")
nparams = append(nparams, b.CreateLoad(ti.Type1, b.CreateStructGEP(typ, iptr, 0, ""), ""))
nparams = append(nparams, b.CreateLoad(ti.Type2, b.CreateStructGEP(typ, iptr, 1, ""), ""))
}
}
updateCallAttr := func(call llvm.Value) {
for i, attr := range attrs {
call.AddCallSiteAttribute(i, attr)
}
}
var instr llvm.Value
switch info.Return.Kind {
case AttrVoid:
instr = llvm.CreateCall(b, nft, nfn, nparams)
updateCallAttr(instr)
case AttrPointer:
ret := llvm.CreateAlloca(b, info.Return.Type)
call := llvm.CreateCall(b, nft, nfn, append([]llvm.Value{ret}, nparams...))
updateCallAttr(call)
instr = b.CreateLoad(info.Return.Type, ret, "")
case AttrWidthType, AttrWidthType2:
ret := llvm.CreateCall(b, nft, nfn, nparams)
updateCallAttr(ret)
ptr := llvm.CreateAlloca(b, nft.ReturnType())
b.CreateStore(ret, ptr)
pret := b.CreateBitCast(ptr, llvm.PointerType(info.Return.Type, 0), "")
instr = b.CreateLoad(info.Return.Type, pret, "")
default:
instr = llvm.CreateCall(b, nft, nfn, nparams)
updateCallAttr(instr)
}
call.ReplaceAllUsesWith(instr)
call.RemoveFromParentAsInstruction()
return true
}
func (p *Transformer) transformFuncCall(m llvm.Module, fn llvm.Value) {
u := fn.FirstUse()
ctx := m.Context()
for !u.IsNil() {
if call := u.User().IsACallInst(); !call.IsNil() {
n := call.OperandsCount()
for i := 0; i < n; i++ {
op := call.Operand(i)
if op == fn {
continue
}
if gv := op.IsAGlobalValue(); !gv.IsNil() {
if ft := gv.GlobalValueType(); ft.TypeKind() == llvm.FunctionTypeKind {
if p.isCFunc(gv.Name()) {
continue
}
if p.isWrapFunctionType(ctx, ft) {
if wrap, ok := p.transformCallbackFunc(m, gv); ok {
call.SetOperand(i, wrap)
}
}
}
}
}
}
u = u.NextUse()
}
}
func (p *Transformer) transformCallbackFunc(m llvm.Module, fn llvm.Value) (wrap llvm.Value, ok bool) {
var paramTypes []llvm.Type
var returnType llvm.Type
attrs := make(map[int]llvm.Attribute)
ctx := m.Context()
info := p.GetFuncInfo(ctx, fn.GlobalValueType())
if !info.HasWrap() {
return fn, false
}
switch info.Return.Kind {
case AttrPointer:
returnType = ctx.VoidType()
paramTypes = append(paramTypes, info.Return.Type1)
attrs[1] = sretAttribute(ctx, info.Return.Type)
case AttrWidthType:
returnType = info.Return.Type1
case AttrWidthType2:
returnType = llvm.StructType([]llvm.Type{info.Return.Type1, info.Return.Type2}, false)
default:
returnType = info.Return.Type1
}
for _, ti := range info.Params {
switch ti.Kind {
case AttrNone, AttrWidthType:
paramTypes = append(paramTypes, ti.Type1)
case AttrPointer:
paramTypes = append(paramTypes, ti.Type1)
if p.sys.SupportByVal() {
attrs[len(paramTypes)] = byvalAttribute(ctx, ti.Type)
}
case AttrWidthType2:
paramTypes = append(paramTypes, ti.Type1, ti.Type2)
}
}
fname := fn.Name()
nft := llvm.FunctionType(returnType, paramTypes, info.Type.IsFunctionVarArg())
wrapName := "__llgo_cdecl$" + fname
if wrapFunc := m.NamedFunction(wrapName); !wrapFunc.IsNil() {
return wrapFunc, true
}
wrapFunc := llvm.AddFunction(m, wrapName, nft)
wrapFunc.SetLinkage(llvm.LinkOnceAnyLinkage)
wrapFunc.AddFunctionAttr(funcInlineHint(ctx))
for i, attr := range attrs {
wrapFunc.AddAttributeAtIndex(i, attr)
}
b := ctx.NewBuilder()
block := llvm.AddBasicBlock(wrapFunc, "entry")
b.SetInsertPointAtEnd(block)
var nparams []llvm.Value
params := wrapFunc.Params()
index := 0
if info.Return.Kind == AttrPointer {
index++
}
for _, ti := range info.Params {
switch ti.Kind {
default:
case AttrPointer:
nparams = append(nparams, b.CreateLoad(ti.Type, params[index], ""))
case AttrWidthType:
iptr := llvm.CreateAlloca(b, ti.Type1)
b.CreateStore(params[index], iptr)
ptr := b.CreateBitCast(iptr, llvm.PointerType(ti.Type, 0), "")
nparams = append(nparams, b.CreateLoad(ti.Type, ptr, ""))
case AttrWidthType2:
typ := llvm.StructType([]llvm.Type{ti.Type1, ti.Type2}, false)
iptr := llvm.CreateAlloca(b, typ)
b.CreateStore(params[index], b.CreateStructGEP(typ, iptr, 0, ""))
index++
b.CreateStore(params[index], b.CreateStructGEP(typ, iptr, 1, ""))
ptr := b.CreateBitCast(iptr, llvm.PointerType(ti.Type, 0), "")
nparams = append(nparams, b.CreateLoad(ti.Type, ptr, ""))
}
index++
}
switch info.Return.Kind {
case AttrVoid:
llvm.CreateCall(b, info.Type, fn, nparams)
b.CreateRetVoid()
case AttrPointer:
ret := llvm.CreateCall(b, info.Type, fn, nparams)
b.CreateStore(ret, params[0])
b.CreateRetVoid()
case AttrWidthType, AttrWidthType2:
ret := llvm.CreateCall(b, info.Type, fn, nparams)
ptr := llvm.CreateAlloca(b, info.Return.Type)
b.CreateStore(ret, ptr)
iptr := b.CreateBitCast(ptr, llvm.PointerType(returnType, 0), "")
b.CreateRet(b.CreateLoad(returnType, iptr, ""))
default:
ret := llvm.CreateCall(b, info.Type, fn, nparams)
b.CreateRet(ret)
}
return wrapFunc, true
}