diff --git a/cl/_testcgo/struct/in.go b/cl/_testcgo/struct/in.go new file mode 100644 index 00000000..74aad81a --- /dev/null +++ b/cl/_testcgo/struct/in.go @@ -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() +} diff --git a/cl/_testcgo/struct/out.ll b/cl/_testcgo/struct/out.ll new file mode 100644 index 00000000..5142a659 --- /dev/null +++ b/cl/_testcgo/struct/out.ll @@ -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, ...) diff --git a/cl/_testdata/method/out.ll b/cl/_testdata/method/out.ll index 8d3f9f96..b4734045 100644 --- a/cl/_testdata/method/out.ll +++ b/cl/_testdata/method/out.ll @@ -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 } diff --git a/cl/_testdata/ptrmthd/out.ll b/cl/_testdata/ptrmthd/out.ll index 77665e9d..25fe6d7a 100644 --- a/cl/_testdata/ptrmthd/out.ll +++ b/cl/_testdata/ptrmthd/out.ll @@ -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 } diff --git a/cl/compile.go b/cl/compile.go index 7c8e2412..5a3a602d 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -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{ diff --git a/cl/import.go b/cl/import.go index 53b42d21..1f351476 100644 --- a/cl/import.go +++ b/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 } diff --git a/cl/intrinsics.go b/cl/intrinsics.go deleted file mode 100644 index 7f34e9e9..00000000 --- a/cl/intrinsics.go +++ /dev/null @@ -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") -} -*/ diff --git a/ssa/expr.go b/ssa/expr.go index 7f040d50..dca906b9 100644 --- a/ssa/expr.go +++ b/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 { diff --git a/ssa/ssa_test.go b/ssa/ssa_test.go index 1eb402cf..448515bf 100644 --- a/ssa/ssa_test.go +++ b/ssa/ssa_test.go @@ -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 `) } diff --git a/ssa/type.go b/ssa/type.go index d9a8716b..0b26d7bb 100644 --- a/ssa/type.go +++ b/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() +} + // -----------------------------------------------------------------------------