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.init$guard" = global ptr null
|
||||
|
||||
define i64 @"(T).Add"(i64 %0, i64 %1) {
|
||||
define i64 @"(main.T).Add"(i64 %0, i64 %1) {
|
||||
_llgo_0:
|
||||
%2 = add i64 %0, %1
|
||||
ret i64 %2
|
||||
}
|
||||
|
||||
define i64 @"(*T).Add"(ptr %0, i64 %1) {
|
||||
define i64 @"(*main.T).Add"(ptr %0, i64 %1) {
|
||||
_llgo_0:
|
||||
%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
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
define void @main() {
|
||||
_llgo_0:
|
||||
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)
|
||||
ret void
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ source_filename = "main"
|
||||
@main.format = 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:
|
||||
call void (ptr, ...) @printf(ptr %0, i64 %1)
|
||||
ret void
|
||||
@@ -36,7 +36,7 @@ _llgo_2: ; preds = %_llgo_1, %_llgo_0
|
||||
define void @main() {
|
||||
_llgo_0:
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ func (p *context) compileType(pkg llssa.Package, t *ssa.Type) {
|
||||
tn := t.Object().(*types.TypeName)
|
||||
tnName := tn.Name()
|
||||
typ := tn.Type()
|
||||
name := fullName(tn.Pkg(), tnName)
|
||||
name := llssa.FullName(tn.Pkg(), tnName)
|
||||
if ignoreName(name) {
|
||||
return
|
||||
}
|
||||
@@ -164,7 +164,7 @@ func (p *context) compileMethods(pkg llssa.Package, typ types.Type) {
|
||||
// Global variable.
|
||||
func (p *context) compileGlobal(pkg llssa.Package, gbl *ssa.Global) {
|
||||
typ := gbl.Type()
|
||||
name := fullName(gbl.Pkg.Pkg, gbl.Name())
|
||||
name := llssa.FullName(gbl.Pkg.Pkg, gbl.Name())
|
||||
if ignoreName(name) || checkCgo(gbl.Name()) {
|
||||
return
|
||||
}
|
||||
@@ -300,6 +300,9 @@ func (p *context) compileInstrAndValue(b llssa.Builder, iv instrAndValue) (ret l
|
||||
t := v.Type()
|
||||
x := p.compileValue(b, v.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:
|
||||
vx := v.X
|
||||
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
|
||||
pkgTypes := pkg.Pkg
|
||||
pkgName, pkgPath := pkgTypes.Name(), pathOf(pkgTypes)
|
||||
pkgName, pkgPath := pkgTypes.Name(), llssa.PathOf(pkgTypes)
|
||||
ret = prog.NewPackage(pkgName, pkgPath)
|
||||
|
||||
ctx := &context{
|
||||
|
||||
19
cl/import.go
19
cl/import.go
@@ -51,7 +51,7 @@ func (p *context) importPkg(pkg *types.Package) {
|
||||
fset := p.fset
|
||||
names := scope.Names()
|
||||
contents := make(contentMap)
|
||||
pkgPath := pathOf(pkg)
|
||||
pkgPath := llssa.PathOf(pkg)
|
||||
for _, name := range names {
|
||||
if token.IsExported(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
|
||||
// method: (pkg.T).name, (*pkg.T).name
|
||||
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 {
|
||||
t, tName = tp.Elem(), "*"
|
||||
}
|
||||
tName += t.(*types.Named).Obj().Name()
|
||||
tName += llssa.NameOf(t.(*types.Named))
|
||||
return "(" + tName + ")." + name
|
||||
}
|
||||
ret := fullName(pkg, name)
|
||||
ret := llssa.FullName(pkg, name)
|
||||
if ret == "main.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 {
|
||||
pkgTypes := p.ensureLoaded(v.Pkg.Pkg)
|
||||
pkg := p.pkg
|
||||
name := fullName(pkgTypes, v.Name())
|
||||
name := llssa.FullName(pkgTypes, v.Name())
|
||||
if ret := pkg.VarOf(name); ret != nil {
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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
|
||||
func (b Builder) Alloc(t Type, heap bool) (ret Expr) {
|
||||
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)
|
||||
if heap {
|
||||
|
||||
@@ -88,9 +88,9 @@ func TestNamedStruct(t *testing.T) {
|
||||
assertPkg(t, pkg, `; ModuleID = '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))
|
||||
}
|
||||
|
||||
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 {
|
||||
if v := p.typs.At(typ); v != nil {
|
||||
return v.(Type)
|
||||
@@ -322,11 +327,27 @@ func (p Program) retType(sig *types.Signature) Type {
|
||||
func (p Program) toLLVMNamed(typ *types.Named) Type {
|
||||
switch t := typ.Underlying().(type) {
|
||||
case *types.Struct:
|
||||
name := typ.Obj().Name()
|
||||
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()
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user