/* * Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ssa import ( "fmt" "go/token" "go/types" "github.com/goplus/llvm" ) var ( tyAny = types.NewInterfaceType(nil, nil) ) // ----------------------------------------------------------------------------- type valueKind = int const ( vkInvalid valueKind = iota vkSigned vkUnsigned vkFloat vkComplex vkString vkBool vkPtr vkFuncDecl // func decl vkFuncPtr // func ptr in C vkClosure // func ptr in Go vkTuple vkDelayExpr = -1 vkPhisExpr = -2 ) // ----------------------------------------------------------------------------- func indexType(t types.Type) types.Type { switch t := t.(type) { case *types.Slice: return t.Elem() case *types.Pointer: switch t := t.Elem().(type) { case *types.Array: return t.Elem() } case *types.Array: return t.Elem() } panic("index: type doesn't support index - " + t.String()) } // convert method to func func methodToFunc(sig *types.Signature) *types.Signature { if recv := sig.Recv(); recv != nil { tParams := sig.Params() nParams := tParams.Len() params := make([]*types.Var, nParams+1) params[0] = recv for i := 0; i < nParams; i++ { params[i+1] = tParams.At(i) } return types.NewSignatureType( nil, nil, nil, types.NewTuple(params...), sig.Results(), sig.Variadic()) } return sig } // ----------------------------------------------------------------------------- type aType struct { ll llvm.Type t types.Type kind valueKind // value kind of llvm.Type } type Type = *aType // TODO(xsw): // how to generate platform independent code? func (p Program) SizeOf(typ Type, n ...int64) uint64 { size := p.td.TypeAllocSize(typ.ll) if len(n) != 0 { size *= uint64(n[0]) } return size } func (p Program) Slice(typ Type) Type { return p.Type(types.NewSlice(typ.t)) } func (p Program) Pointer(typ Type) Type { return p.Type(types.NewPointer(typ.t)) } func (p Program) Elem(typ Type) Type { elem := typ.t.(*types.Pointer).Elem() return p.Type(elem) } func (p Program) Index(typ Type) Type { return p.Type(indexType(typ.t)) } func (p Program) Field(typ Type, i int) Type { tunder := typ.t.Underlying() tfld := tunder.(*types.Struct).Field(i).Type() return p.Type(tfld) } func (p Program) Type(typ types.Type) Type { if v := p.typs.At(typ); v != nil { return v.(Type) } ret := p.toLLVMType(typ) p.typs.Set(typ, ret) return ret } func (p Program) llvmFuncDecl(sig *types.Signature) Type { sig = methodToFunc(sig) return p.toLLVMFunc(sig, false, true) // don't save func decl to cache } func (p Program) tyVoidPtr() llvm.Type { if p.voidPtrTy.IsNil() { p.voidPtrTy = llvm.PointerType(p.tyVoid(), 0) } return p.voidPtrTy } func (p Program) tyVoid() llvm.Type { if p.voidType.IsNil() { p.voidType = p.ctx.VoidType() } return p.voidType } func (p Program) tyInt1() llvm.Type { if p.int1Type.IsNil() { p.int1Type = p.ctx.Int1Type() } return p.int1Type } func (p Program) tyInt() llvm.Type { if p.intType.IsNil() { p.intType = llvmIntType(p.ctx, p.td.PointerSize()) } return p.intType } func llvmIntType(ctx llvm.Context, size int) llvm.Type { if size <= 4 { return ctx.Int32Type() } return ctx.Int64Type() } func (p Program) tyInt8() llvm.Type { if p.int8Type.IsNil() { p.int8Type = p.ctx.Int8Type() } return p.int8Type } func (p Program) tyInt16() llvm.Type { if p.int16Type.IsNil() { p.int16Type = p.ctx.Int16Type() } return p.int16Type } func (p Program) tyInt32() llvm.Type { if p.int32Type.IsNil() { p.int32Type = p.ctx.Int32Type() } return p.int32Type } func (p Program) tyInt64() llvm.Type { if p.int64Type.IsNil() { p.int64Type = p.ctx.Int64Type() } return p.int64Type } func (p Program) toLLVMType(typ types.Type) Type { switch t := typ.(type) { case *types.Basic: switch t.Kind() { case types.Int: return &aType{p.tyInt(), typ, vkSigned} case types.Uint, types.Uintptr: return &aType{p.tyInt(), typ, vkUnsigned} case types.Bool: return &aType{p.tyInt1(), typ, vkBool} case types.Uint8: return &aType{p.tyInt8(), typ, vkUnsigned} case types.Int8: return &aType{p.tyInt8(), typ, vkSigned} case types.Int16: return &aType{p.tyInt16(), typ, vkSigned} case types.Uint16: return &aType{p.tyInt16(), typ, vkUnsigned} case types.Int32: return &aType{p.tyInt32(), typ, vkSigned} case types.Uint32: return &aType{p.tyInt32(), typ, vkUnsigned} case types.Int64: return &aType{p.tyInt64(), typ, vkSigned} case types.Uint64: return &aType{p.tyInt64(), typ, vkUnsigned} case types.Float32: return &aType{p.ctx.FloatType(), typ, vkFloat} case types.Float64: return &aType{p.ctx.DoubleType(), typ, vkFloat} case types.Complex64: case types.Complex128: case types.String: return &aType{p.rtString(), typ, vkString} case types.UnsafePointer: return &aType{p.tyVoidPtr(), typ, vkPtr} } case *types.Pointer: elem := p.Type(t.Elem()) return &aType{llvm.PointerType(elem.ll, 0), typ, vkPtr} case *types.Interface: return &aType{p.rtIface(), typ, vkInvalid} case *types.Slice: return &aType{p.rtSlice(), typ, vkInvalid} case *types.Map: return &aType{p.rtMap(), typ, vkInvalid} case *types.Struct: return p.toLLVMStruct(t) case *types.Named: return p.toLLVMNamed(t) case *types.Signature: return p.toLLVMFunc(t, false, false) case *CFuncPtr: return p.toLLVMFunc((*types.Signature)(t), true, false) case *types.Array: elem := p.Type(t.Elem()) return &aType{llvm.ArrayType(elem.ll, int(t.Len())), typ, vkInvalid} case *types.Chan: } panic(fmt.Sprintf("toLLVMType: todo - %T\n", typ)) } func (p Program) toLLVMNamedStruct(name string, typ *types.Struct) llvm.Type { t := p.ctx.StructCreateNamed(name) fields := p.toLLVMFields(typ) t.StructSetBody(fields, false) return t } func (p Program) toLLVMStruct(typ *types.Struct) Type { fields := p.toLLVMFields(typ) return &aType{p.ctx.StructType(fields, false), typ, vkInvalid} } func (p Program) toLLVMFields(typ *types.Struct) (fields []llvm.Type) { n := typ.NumFields() if n > 0 { fields = make([]llvm.Type, n) for i := 0; i < n; i++ { fields[i] = p.Type(typ.Field(i).Type()).ll } } return } func (p Program) toLLVMTuple(t *types.Tuple) llvm.Type { return p.ctx.StructType(p.toLLVMTypes(t, t.Len()), false) } func (p Program) toLLVMTypes(t *types.Tuple, n int) (ret []llvm.Type) { if n > 0 { ret = make([]llvm.Type, n) for i := 0; i < n; i++ { ret[i] = p.Type(t.At(i).Type()).ll } } return } func (p Program) toLLVMFunc(sig *types.Signature, inC, isDecl bool) Type { if isDecl || inC { tParams := sig.Params() n := tParams.Len() hasVArg := HasVArg(tParams, n) if hasVArg { n-- } params := p.toLLVMTypes(tParams, n) out := sig.Results() var ret llvm.Type var kind valueKind switch nret := out.Len(); nret { case 0: ret = p.tyVoid() case 1: ret = p.Type(out.At(0).Type()).ll default: ret = p.toLLVMTuple(out) } ft := llvm.FunctionType(ret, params, hasVArg) if isDecl { kind = vkFuncDecl } else { ft = llvm.PointerType(ft, 0) kind = vkFuncPtr } return &aType{ft, sig, kind} } flds := []*types.Var{ types.NewField(token.NoPos, nil, "f", (*CFuncPtr)(sig), false), types.NewField(token.NoPos, nil, "data", types.Typ[types.UnsafePointer], false), } t := types.NewStruct(flds, nil) ll := p.ctx.StructType(p.toLLVMFields(t), false) return &aType{ll, t, vkClosure} } func (p Program) retType(sig *types.Signature) Type { out := sig.Results() switch n := out.Len(); n { case 0: return p.Void() case 1: return p.Type(out.At(0).Type()) default: return &aType{p.toLLVMTuple(out), out, vkTuple} } } func (p Program) toLLVMNamed(typ *types.Named) Type { switch t := typ.Underlying().(type) { case *types.Struct: name := NameOf(typ) return &aType{p.toLLVMNamedStruct(name, t), typ, vkInvalid} default: return p.Type(t) } } func NameOf(typ *types.Named) string { obj := typ.Obj() return FullName(obj.Pkg(), obj.Name()) } func FullName(pkg *types.Package, name string) string { return PathOf(pkg) + "." + name } func PathOf(pkg *types.Package) string { if pkg.Name() == "main" { return "main" } return pkg.Path() } // -----------------------------------------------------------------------------