internal/cabi: transform pkg to cabi
This commit is contained in:
356
internal/cabi/arch.go
Normal file
356
internal/cabi/arch.go
Normal 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
545
internal/cabi/cabi.go
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user