25
cl/_testcgo/struct/in.go
Normal file
25
cl/_testcgo/struct/in.go
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "C"
|
||||||
|
import _ "unsafe"
|
||||||
|
|
||||||
|
//go:linkname printf C.printf
|
||||||
|
func printf(format *int8, __llgo_va_list ...any)
|
||||||
|
|
||||||
|
type Foo struct {
|
||||||
|
A C.int
|
||||||
|
ok bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var format = [...]int8{'H', 'e', 'l', 'l', 'o', ' ', '%', 'd', '\n', 0}
|
||||||
|
|
||||||
|
func (p *Foo) Print() {
|
||||||
|
if p.ok {
|
||||||
|
printf(&format[0], p.A)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
foo := Foo{100, true}
|
||||||
|
foo.Print()
|
||||||
|
}
|
||||||
60
cl/_testcgo/struct/out.ll
Normal file
60
cl/_testcgo/struct/out.ll
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
; ModuleID = 'main'
|
||||||
|
source_filename = "main"
|
||||||
|
|
||||||
|
%main.Foo = type { i32, i1 }
|
||||||
|
|
||||||
|
@main.format = global ptr null
|
||||||
|
@"main.init$guard" = global ptr null
|
||||||
|
|
||||||
|
define void @"(*main.Foo).Print"(ptr %0) {
|
||||||
|
_llgo_0:
|
||||||
|
%1 = getelementptr inbounds %main.Foo, ptr %0, i32 0, i32 1
|
||||||
|
%2 = load i1, ptr %1, align 1
|
||||||
|
br i1 %2, label %_llgo_1, label %_llgo_2
|
||||||
|
|
||||||
|
_llgo_1: ; preds = %_llgo_0
|
||||||
|
%3 = getelementptr inbounds %main.Foo, ptr %0, i32 0, i32 0
|
||||||
|
%4 = load i32, ptr %3, align 4
|
||||||
|
call void (ptr, ...) @printf(ptr @main.format, i32 %4)
|
||||||
|
br label %_llgo_2
|
||||||
|
|
||||||
|
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define void @main.init() {
|
||||||
|
_llgo_0:
|
||||||
|
%0 = load i1, ptr @"main.init$guard", align 1
|
||||||
|
br i1 %0, label %_llgo_2, label %_llgo_1
|
||||||
|
|
||||||
|
_llgo_1: ; preds = %_llgo_0
|
||||||
|
store i1 true, ptr @"main.init$guard", align 1
|
||||||
|
store i8 72, ptr @main.format, align 1
|
||||||
|
store i8 101, ptr getelementptr inbounds (i8, ptr @main.format, i64 1), align 1
|
||||||
|
store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 2), align 1
|
||||||
|
store i8 108, ptr getelementptr inbounds (i8, ptr @main.format, i64 3), align 1
|
||||||
|
store i8 111, ptr getelementptr inbounds (i8, ptr @main.format, i64 4), align 1
|
||||||
|
store i8 32, ptr getelementptr inbounds (i8, ptr @main.format, i64 5), align 1
|
||||||
|
store i8 37, ptr getelementptr inbounds (i8, ptr @main.format, i64 6), align 1
|
||||||
|
store i8 100, ptr getelementptr inbounds (i8, ptr @main.format, i64 7), align 1
|
||||||
|
store i8 10, ptr getelementptr inbounds (i8, ptr @main.format, i64 8), align 1
|
||||||
|
store i8 0, ptr getelementptr inbounds (i8, ptr @main.format, i64 9), align 1
|
||||||
|
br label %_llgo_2
|
||||||
|
|
||||||
|
_llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
define void @main() {
|
||||||
|
_llgo_0:
|
||||||
|
call void @main.init()
|
||||||
|
%0 = alloca %main.Foo, align 8
|
||||||
|
%1 = getelementptr inbounds %main.Foo, ptr %0, i32 0, i32 0
|
||||||
|
%2 = getelementptr inbounds %main.Foo, ptr %0, i32 0, i32 1
|
||||||
|
store i32 100, ptr %1, align 4
|
||||||
|
store i1 true, ptr %2, align 1
|
||||||
|
call void @"(*main.Foo).Print"(ptr %0)
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
||||||
|
declare void @printf(ptr, ...)
|
||||||
@@ -4,16 +4,16 @@ source_filename = "main"
|
|||||||
@main.format = global ptr null
|
@main.format = global ptr null
|
||||||
@"main.init$guard" = global ptr null
|
@"main.init$guard" = global ptr null
|
||||||
|
|
||||||
define i64 @"(T).Add"(i64 %0, i64 %1) {
|
define i64 @"(main.T).Add"(i64 %0, i64 %1) {
|
||||||
_llgo_0:
|
_llgo_0:
|
||||||
%2 = add i64 %0, %1
|
%2 = add i64 %0, %1
|
||||||
ret i64 %2
|
ret i64 %2
|
||||||
}
|
}
|
||||||
|
|
||||||
define i64 @"(*T).Add"(ptr %0, i64 %1) {
|
define i64 @"(*main.T).Add"(ptr %0, i64 %1) {
|
||||||
_llgo_0:
|
_llgo_0:
|
||||||
%2 = load i64, ptr %0, align 4
|
%2 = load i64, ptr %0, align 4
|
||||||
%3 = call i64 @"(T).Add"(i64 %2, i64 %1)
|
%3 = call i64 @"(main.T).Add"(i64 %2, i64 %1)
|
||||||
ret i64 %3
|
ret i64 %3
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0
|
|||||||
define void @main() {
|
define void @main() {
|
||||||
_llgo_0:
|
_llgo_0:
|
||||||
call void @main.init()
|
call void @main.init()
|
||||||
%0 = call i64 @"(T).Add"(i64 1, i64 2)
|
%0 = call i64 @"(main.T).Add"(i64 1, i64 2)
|
||||||
call void (ptr, ...) @printf(ptr @main.format, i64 %0)
|
call void (ptr, ...) @printf(ptr @main.format, i64 %0)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ source_filename = "main"
|
|||||||
@main.format = global ptr null
|
@main.format = global ptr null
|
||||||
@"main.init$guard" = global ptr null
|
@"main.init$guard" = global ptr null
|
||||||
|
|
||||||
define void @"(*T).Print"(ptr %0, i64 %1) {
|
define void @"(*main.T).Print"(ptr %0, i64 %1) {
|
||||||
_llgo_0:
|
_llgo_0:
|
||||||
call void (ptr, ...) @printf(ptr %0, i64 %1)
|
call void (ptr, ...) @printf(ptr %0, i64 %1)
|
||||||
ret void
|
ret void
|
||||||
@@ -36,7 +36,7 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0
|
|||||||
define void @main() {
|
define void @main() {
|
||||||
_llgo_0:
|
_llgo_0:
|
||||||
call void @main.init()
|
call void @main.init()
|
||||||
call void @"(*T).Print"(ptr @main.format, i64 100)
|
call void @"(*main.T).Print"(ptr @main.format, i64 100)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ func (p *context) compileType(pkg llssa.Package, t *ssa.Type) {
|
|||||||
tn := t.Object().(*types.TypeName)
|
tn := t.Object().(*types.TypeName)
|
||||||
tnName := tn.Name()
|
tnName := tn.Name()
|
||||||
typ := tn.Type()
|
typ := tn.Type()
|
||||||
name := fullName(tn.Pkg(), tnName)
|
name := llssa.FullName(tn.Pkg(), tnName)
|
||||||
if ignoreName(name) {
|
if ignoreName(name) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -164,7 +164,7 @@ func (p *context) compileMethods(pkg llssa.Package, typ types.Type) {
|
|||||||
// Global variable.
|
// Global variable.
|
||||||
func (p *context) compileGlobal(pkg llssa.Package, gbl *ssa.Global) {
|
func (p *context) compileGlobal(pkg llssa.Package, gbl *ssa.Global) {
|
||||||
typ := gbl.Type()
|
typ := gbl.Type()
|
||||||
name := fullName(gbl.Pkg.Pkg, gbl.Name())
|
name := llssa.FullName(gbl.Pkg.Pkg, gbl.Name())
|
||||||
if ignoreName(name) || checkCgo(gbl.Name()) {
|
if ignoreName(name) || checkCgo(gbl.Name()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -300,6 +300,9 @@ func (p *context) compileInstrAndValue(b llssa.Builder, iv instrAndValue) (ret l
|
|||||||
t := v.Type()
|
t := v.Type()
|
||||||
x := p.compileValue(b, v.X)
|
x := p.compileValue(b, v.X)
|
||||||
ret = b.ChangeType(p.prog.Type(t), x)
|
ret = b.ChangeType(p.prog.Type(t), x)
|
||||||
|
case *ssa.FieldAddr:
|
||||||
|
x := p.compileValue(b, v.X)
|
||||||
|
ret = b.FieldAddr(x, v.Field)
|
||||||
case *ssa.IndexAddr:
|
case *ssa.IndexAddr:
|
||||||
vx := v.X
|
vx := v.X
|
||||||
if _, ok := p.isVArgs(vx); ok { // varargs: this is a varargs index
|
if _, ok := p.isVArgs(vx); ok { // varargs: this is a varargs index
|
||||||
@@ -452,7 +455,7 @@ func NewPackage(prog llssa.Program, pkg *ssa.Package, files []*ast.File) (ret ll
|
|||||||
|
|
||||||
pkgProg := pkg.Prog
|
pkgProg := pkg.Prog
|
||||||
pkgTypes := pkg.Pkg
|
pkgTypes := pkg.Pkg
|
||||||
pkgName, pkgPath := pkgTypes.Name(), pathOf(pkgTypes)
|
pkgName, pkgPath := pkgTypes.Name(), llssa.PathOf(pkgTypes)
|
||||||
ret = prog.NewPackage(pkgName, pkgPath)
|
ret = prog.NewPackage(pkgName, pkgPath)
|
||||||
|
|
||||||
ctx := &context{
|
ctx := &context{
|
||||||
|
|||||||
19
cl/import.go
19
cl/import.go
@@ -51,7 +51,7 @@ func (p *context) importPkg(pkg *types.Package) {
|
|||||||
fset := p.fset
|
fset := p.fset
|
||||||
names := scope.Names()
|
names := scope.Names()
|
||||||
contents := make(contentMap)
|
contents := make(contentMap)
|
||||||
pkgPath := pathOf(pkg)
|
pkgPath := llssa.PathOf(pkg)
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
if token.IsExported(name) {
|
if token.IsExported(name) {
|
||||||
obj := scope.Lookup(name)
|
obj := scope.Lookup(name)
|
||||||
@@ -109,17 +109,6 @@ func (p *context) initLinkname(pkgPath, line string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func pathOf(pkg *types.Package) string {
|
|
||||||
if pkg.Name() == "main" {
|
|
||||||
return "main"
|
|
||||||
}
|
|
||||||
return pkg.Path()
|
|
||||||
}
|
|
||||||
|
|
||||||
func fullName(pkg *types.Package, name string) string {
|
|
||||||
return pathOf(pkg) + "." + name
|
|
||||||
}
|
|
||||||
|
|
||||||
// func: pkg.name
|
// func: pkg.name
|
||||||
// method: (pkg.T).name, (*pkg.T).name
|
// method: (pkg.T).name, (*pkg.T).name
|
||||||
func funcName(pkg *types.Package, fn *ssa.Function) string {
|
func funcName(pkg *types.Package, fn *ssa.Function) string {
|
||||||
@@ -131,10 +120,10 @@ func funcName(pkg *types.Package, fn *ssa.Function) string {
|
|||||||
if tp, ok := t.(*types.Pointer); ok {
|
if tp, ok := t.(*types.Pointer); ok {
|
||||||
t, tName = tp.Elem(), "*"
|
t, tName = tp.Elem(), "*"
|
||||||
}
|
}
|
||||||
tName += t.(*types.Named).Obj().Name()
|
tName += llssa.NameOf(t.(*types.Named))
|
||||||
return "(" + tName + ")." + name
|
return "(" + tName + ")." + name
|
||||||
}
|
}
|
||||||
ret := fullName(pkg, name)
|
ret := llssa.FullName(pkg, name)
|
||||||
if ret == "main.main" {
|
if ret == "main.main" {
|
||||||
ret = "main"
|
ret = "main"
|
||||||
}
|
}
|
||||||
@@ -171,7 +160,7 @@ func (p *context) funcOf(fn *ssa.Function) llssa.Function {
|
|||||||
func (p *context) varOf(v *ssa.Global) llssa.Global {
|
func (p *context) varOf(v *ssa.Global) llssa.Global {
|
||||||
pkgTypes := p.ensureLoaded(v.Pkg.Pkg)
|
pkgTypes := p.ensureLoaded(v.Pkg.Pkg)
|
||||||
pkg := p.pkg
|
pkg := p.pkg
|
||||||
name := fullName(pkgTypes, v.Name())
|
name := llssa.FullName(pkgTypes, v.Name())
|
||||||
if ret := pkg.VarOf(name); ret != nil {
|
if ret := pkg.VarOf(name); ret != nil {
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2023 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 cl
|
|
||||||
|
|
||||||
/*
|
|
||||||
// Define unimplemented intrinsic functions.
|
|
||||||
//
|
|
||||||
// Some functions are either normally implemented in Go assembly (like
|
|
||||||
// sync/atomic functions) or intentionally left undefined to be implemented
|
|
||||||
// directly in the compiler (like runtime/volatile functions). Either way, look
|
|
||||||
// for these and implement them if this is the case.
|
|
||||||
func (b *builder) defineIntrinsicFunction() {
|
|
||||||
panic("todo")
|
|
||||||
}
|
|
||||||
|
|
||||||
var mathToLLVMMapping = map[string]string{
|
|
||||||
"math.Ceil": "llvm.ceil.f64",
|
|
||||||
"math.Exp": "llvm.exp.f64",
|
|
||||||
"math.Exp2": "llvm.exp2.f64",
|
|
||||||
"math.Floor": "llvm.floor.f64",
|
|
||||||
"math.Log": "llvm.log.f64",
|
|
||||||
"math.Sqrt": "llvm.sqrt.f64",
|
|
||||||
"math.Trunc": "llvm.trunc.f64",
|
|
||||||
}
|
|
||||||
|
|
||||||
// defineMathOp defines a math function body as a call to a LLVM intrinsic,
|
|
||||||
// instead of the regular Go implementation. This allows LLVM to reason about
|
|
||||||
// the math operation and (depending on the architecture) allows it to lower the
|
|
||||||
// operation to very fast floating point instructions. If this is not possible,
|
|
||||||
// LLVM will emit a call to a libm function that implements the same operation.
|
|
||||||
//
|
|
||||||
// One example of an optimization that LLVM can do is to convert
|
|
||||||
// float32(math.Sqrt(float64(v))) to a 32-bit floating point operation, which is
|
|
||||||
// beneficial on architectures where 64-bit floating point operations are (much)
|
|
||||||
// more expensive than 32-bit ones.
|
|
||||||
func (b *builder) defineMathOp() {
|
|
||||||
panic("todo")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implement most math/bits functions.
|
|
||||||
//
|
|
||||||
// This implements all the functions that operate on bits. It does not yet
|
|
||||||
// implement the arithmetic functions (like bits.Add), which also have LLVM
|
|
||||||
// intrinsics.
|
|
||||||
func (b *builder) defineMathBitsIntrinsic() bool {
|
|
||||||
panic("todo")
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
32
ssa/expr.go
32
ssa/expr.go
@@ -280,6 +280,36 @@ func (b Builder) Store(ptr, val Expr) Builder {
|
|||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The FieldAddr instruction yields the address of Field of *struct X.
|
||||||
|
//
|
||||||
|
// The field is identified by its index within the field list of the
|
||||||
|
// struct type of X.
|
||||||
|
//
|
||||||
|
// Dynamically, this instruction panics if X evaluates to a nil
|
||||||
|
// pointer.
|
||||||
|
//
|
||||||
|
// Type() returns a (possibly named) *types.Pointer.
|
||||||
|
//
|
||||||
|
// Pos() returns the position of the ast.SelectorExpr.Sel for the
|
||||||
|
// field, if explicit in the source. For implicit selections, returns
|
||||||
|
// the position of the inducing explicit selection. If produced for a
|
||||||
|
// struct literal S{f: e}, it returns the position of the colon; for
|
||||||
|
// S{e} it returns the start of expression e.
|
||||||
|
//
|
||||||
|
// Example printed form:
|
||||||
|
//
|
||||||
|
// t1 = &t0.name [#1]
|
||||||
|
func (b Builder) FieldAddr(x Expr, idx int) Expr {
|
||||||
|
if debugInstr {
|
||||||
|
log.Printf("FieldAddr %v, %d\n", x.impl, idx)
|
||||||
|
}
|
||||||
|
prog := b.prog
|
||||||
|
tstruc := prog.Elem(x.Type)
|
||||||
|
telem := prog.Field(tstruc, idx)
|
||||||
|
pt := prog.Pointer(telem)
|
||||||
|
return Expr{llvm.CreateStructGEP(b.impl, tstruc.ll, x.impl, idx), pt}
|
||||||
|
}
|
||||||
|
|
||||||
// The IndexAddr instruction yields the address of the element at
|
// The IndexAddr instruction yields the address of the element at
|
||||||
// index `idx` of collection `x`. `idx` is an integer expression.
|
// index `idx` of collection `x`. `idx` is an integer expression.
|
||||||
//
|
//
|
||||||
@@ -324,7 +354,7 @@ func (b Builder) IndexAddr(x, idx Expr) Expr {
|
|||||||
// t1 = new int
|
// t1 = new int
|
||||||
func (b Builder) Alloc(t Type, heap bool) (ret Expr) {
|
func (b Builder) Alloc(t Type, heap bool) (ret Expr) {
|
||||||
if debugInstr {
|
if debugInstr {
|
||||||
log.Printf("Alloc %v, %v\n", t.ll, heap)
|
log.Printf("Alloc %v, %v\n", t.t, heap)
|
||||||
}
|
}
|
||||||
telem := b.prog.Elem(t)
|
telem := b.prog.Elem(t)
|
||||||
if heap {
|
if heap {
|
||||||
|
|||||||
@@ -88,9 +88,9 @@ func TestNamedStruct(t *testing.T) {
|
|||||||
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
assertPkg(t, pkg, `; ModuleID = 'foo/bar'
|
||||||
source_filename = "foo/bar"
|
source_filename = "foo/bar"
|
||||||
|
|
||||||
%Empty = type {}
|
%bar.Empty = type {}
|
||||||
|
|
||||||
@a = external global %Empty
|
@a = external global %bar.Empty
|
||||||
`)
|
`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
23
ssa/type.go
23
ssa/type.go
@@ -111,6 +111,11 @@ func (p Program) Index(typ Type) Type {
|
|||||||
return p.Type(indexType(typ.t))
|
return p.Type(indexType(typ.t))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p Program) Field(typ Type, i int) Type {
|
||||||
|
tunder := typ.t.Underlying()
|
||||||
|
return p.Type(tunder.(*types.Struct).Field(i).Type())
|
||||||
|
}
|
||||||
|
|
||||||
func (p Program) Type(typ types.Type) Type {
|
func (p Program) Type(typ types.Type) Type {
|
||||||
if v := p.typs.At(typ); v != nil {
|
if v := p.typs.At(typ); v != nil {
|
||||||
return v.(Type)
|
return v.(Type)
|
||||||
@@ -322,11 +327,27 @@ func (p Program) retType(sig *types.Signature) Type {
|
|||||||
func (p Program) toLLVMNamed(typ *types.Named) Type {
|
func (p Program) toLLVMNamed(typ *types.Named) Type {
|
||||||
switch t := typ.Underlying().(type) {
|
switch t := typ.Underlying().(type) {
|
||||||
case *types.Struct:
|
case *types.Struct:
|
||||||
name := typ.Obj().Name()
|
name := NameOf(typ)
|
||||||
return &aType{p.toLLVMNamedStruct(name, t), typ, vkInvalid}
|
return &aType{p.toLLVMNamedStruct(name, t), typ, vkInvalid}
|
||||||
default:
|
default:
|
||||||
return p.Type(t)
|
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()
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user