From 28dd34a136e729b1d0805fa5a0200e80fa8465fa Mon Sep 17 00:00:00 2001 From: xushiwei Date: Fri, 26 Apr 2024 00:31:02 +0800 Subject: [PATCH] cl: _testdata/ptrmthd --- cl/_testdata/method/out.ll | 7 +++++ cl/_testdata/ptrmthd/in.go | 19 +++++++++++++ cl/_testdata/ptrmthd/out.ll | 43 ++++++++++++++++++++++++++++ cl/compile.go | 45 ++++++++++++++++++++++------- ssa/expr.go | 57 +++++++++++++++++++++++++++++++++++++ ssa/type.go | 32 ++++++++++++--------- 6 files changed, 178 insertions(+), 25 deletions(-) create mode 100644 cl/_testdata/ptrmthd/in.go create mode 100644 cl/_testdata/ptrmthd/out.ll diff --git a/cl/_testdata/method/out.ll b/cl/_testdata/method/out.ll index 8416147c..d5df9805 100644 --- a/cl/_testdata/method/out.ll +++ b/cl/_testdata/method/out.ll @@ -33,6 +33,13 @@ _llgo_0: ret i64 %2 } +define i64 @"(*main.T).Add"(ptr %0, i64 %1) { +_llgo_0: + %2 = load i64, ptr %0, align 4 + %3 = call i64 @"(main.T).Add"(i64 %2, i64 %1) + ret i64 %3 +} + declare void @printf(ptr, ...) define void @main() { diff --git a/cl/_testdata/ptrmthd/in.go b/cl/_testdata/ptrmthd/in.go new file mode 100644 index 00000000..16ee3b42 --- /dev/null +++ b/cl/_testdata/ptrmthd/in.go @@ -0,0 +1,19 @@ +package main + +import _ "unsafe" + +//go:linkname printf printf +func printf(format *int8, __llgo_va_list ...any) + +type T int8 + +func (f *T) Print(v int) { + printf((*int8)(f), v) +} + +var format = [...]T{'H', 'e', 'l', 'l', 'o', ' ', '%', 'd', '\n', 0} + +func main() { + f := &format[0] + f.Print(100) +} diff --git a/cl/_testdata/ptrmthd/out.ll b/cl/_testdata/ptrmthd/out.ll new file mode 100644 index 00000000..32cd1d0e --- /dev/null +++ b/cl/_testdata/ptrmthd/out.ll @@ -0,0 +1,43 @@ +; ModuleID = 'main' +source_filename = "main" + +@"main.init$guard" = global ptr null +@main.format = global ptr null + +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 +} + +declare void @printf(ptr, ...) + +define void @"(*main.T).Print"(ptr %0, i64 %1) { +_llgo_0: + call void (ptr, ...) @printf(ptr %0, i64 %1) + ret void +} + +define void @main() { +_llgo_0: + call void @main.init() + call void @"(*main.T).Print"(ptr @main.format, i64 100) + ret void +} diff --git a/cl/compile.go b/cl/compile.go index 964906ed..6f435e8c 100644 --- a/cl/compile.go +++ b/cl/compile.go @@ -109,12 +109,17 @@ func (p *context) compileType(pkg llssa.Package, t *ssa.Type) { if debugInstr { log.Println("==> NewType", name, typ) } + p.compileMethods(pkg, typ) + p.compileMethods(pkg, types.NewPointer(typ)) +} + +func (p *context) compileMethods(pkg llssa.Package, typ types.Type) { prog := p.goProg mthds := prog.MethodSets.MethodSet(typ) for i, n := 0, mthds.Len(); i < n; i++ { mthd := mthds.At(i) ssaMthd := prog.MethodValue(mthd) - p.compileFunc(pkg, ssaMthd) + p.compileFunc(pkg, mthd.Obj().Pkg(), ssaMthd) } } @@ -129,17 +134,18 @@ func (p *context) compileGlobal(pkg llssa.Package, gbl *ssa.Global) { g.Init(p.prog.Null(g.Type)) } -func (p *context) compileFunc(pkg llssa.Package, f *ssa.Function) { - name := p.funcName(f.Pkg.Pkg, f) +func (p *context) compileFunc(pkg llssa.Package, pkgTypes *types.Package, f *ssa.Function) { + sig := f.Signature + name := p.funcName(pkgTypes, f) /* TODO(xsw): confirm this is not needed more if name == "unsafe.init" { return } */ if debugInstr { - log.Println("==> NewFunc", name, f.Signature) + log.Println("==> NewFunc", name, "type:", sig.Recv(), sig) } - fn := pkg.NewFunc(name, f.Signature) + fn := pkg.NewFunc(name, sig) p.inits = append(p.inits, func() { p.fn = fn defer func() { @@ -221,16 +227,29 @@ func (p *context) compileInstrAndValue(b llssa.Builder, iv instrAndValue) (ret l switch v := iv.(type) { case *ssa.Call: call := v.Call - kind := funcKind(call.Value) + cv := call.Value + kind := funcKind(cv) if kind == fnUnsafeInit { return } if debugGoSSA { - log.Println(">>> Call", call.Value, call.Args) + log.Println(">>> Call", cv, call.Args) + } + if builtin, ok := cv.(*ssa.Builtin); ok { + fn := builtin.Name() + if fn == "ssa:wrapnilchk" { // TODO(xsw): check nil ptr + arg := call.Args[0] + ret = p.compileValue(b, arg) + // log.Println("wrapnilchk:", ret.TypeOf()) + } else { + args := p.compileValues(b, call.Args, kind) + ret = b.BuiltinCall(fn, args...) + } + } else { + fn := p.compileValue(b, cv) + args := p.compileValues(b, call.Args, kind) + ret = b.Call(fn, args...) } - fn := p.compileValue(b, call.Value) - args := p.compileValues(b, call.Args, kind) - ret = b.Call(fn, args...) case *ssa.BinOp: x := p.compileValue(b, v.X) y := p.compileValue(b, v.Y) @@ -238,6 +257,10 @@ func (p *context) compileInstrAndValue(b llssa.Builder, iv instrAndValue) (ret l case *ssa.UnOp: x := p.compileValue(b, v.X) ret = b.UnOp(v.Op, x) + case *ssa.ChangeType: + t := v.Type() + x := p.compileValue(b, v.X) + ret = b.ChangeType(p.prog.Type(t), x) case *ssa.IndexAddr: vx := v.X if _, ok := p.isVArgs(vx); ok { // varargs: this is a varargs index @@ -418,7 +441,7 @@ func NewPackage(prog llssa.Program, pkg *ssa.Package, files []*ast.File) (ret ll // Do not try to build generic (non-instantiated) functions. continue } - ctx.compileFunc(ret, member) + ctx.compileFunc(ret, member.Pkg.Pkg, member) case *ssa.Type: ctx.compileType(ret, member) case *ssa.Global: diff --git a/ssa/expr.go b/ssa/expr.go index 45bcdcf2..7f040d50 100644 --- a/ssa/expr.go +++ b/ssa/expr.go @@ -34,6 +34,13 @@ type Expr struct { Type } +/* +// TypeOf returns the type of the expression. +func (v Expr) TypeOf() types.Type { + return v.t +} +*/ + // ----------------------------------------------------------------------------- func llvmValues(vals []Expr) []llvm.Value { @@ -330,6 +337,46 @@ func (b Builder) Alloc(t Type, heap bool) (ret Expr) { return } +// The ChangeType instruction applies to X a value-preserving type +// change to Type(). +// +// Type changes are permitted: +// - between a named type and its underlying type. +// - between two named types of the same underlying type. +// - between (possibly named) pointers to identical base types. +// - from a bidirectional channel to a read- or write-channel, +// optionally adding/removing a name. +// - between a type (t) and an instance of the type (tσ), i.e. +// Type() == σ(X.Type()) (or X.Type()== σ(Type())) where +// σ is the type substitution of Parent().TypeParams by +// Parent().TypeArgs. +// +// This operation cannot fail dynamically. +// +// Type changes may to be to or from a type parameter (or both). All +// types in the type set of X.Type() have a value-preserving type +// change to all types in the type set of Type(). +// +// Pos() returns the ast.CallExpr.Lparen, if the instruction arose +// from an explicit conversion in the source. +// +// Example printed form: +// +// t1 = changetype *int <- IntPtr (t0) +func (b Builder) ChangeType(t Type, x Expr) (ret Expr) { + if debugInstr { + log.Printf("ChangeType %v, %v\n", t.t, x.impl) + } + typ := t.t + switch typ.(type) { + case *types.Pointer: + ret.impl = b.impl.CreatePointerCast(x.impl, t.ll, "castPtr") + ret.Type = b.prog.Type(typ) + return + } + panic("todo") +} + // ----------------------------------------------------------------------------- // The Call instruction represents a function or method call. @@ -362,4 +409,14 @@ func (b Builder) Call(fn Expr, args ...Expr) (ret Expr) { return } +// A Builtin represents a specific use of a built-in function, e.g. len. +// +// Builtins are immutable values. Builtins do not have addresses. +// +// `fn` indicates the function: one of the built-in functions from the +// Go spec (excluding "make" and "new"). +func (b Builder) BuiltinCall(fn string, args ...Expr) (ret Expr) { + panic("todo") +} + // ----------------------------------------------------------------------------- diff --git a/ssa/type.go b/ssa/type.go index 832846b8..d9a8716b 100644 --- a/ssa/type.go +++ b/ssa/type.go @@ -72,6 +72,22 @@ func indexType(t types.Type) types.Type { 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 { @@ -105,24 +121,12 @@ func (p Program) Type(typ types.Type) Type { } func (p Program) llvmSignature(sig *types.Signature) Type { + sig = methodToFunc(sig) if v := p.typs.At(sig); v != nil { return v.(Type) } - sigOrg := sig - if recv := sig.Recv(); recv != nil { - // convert method to func - 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) - } - sig = types.NewSignatureType( - nil, nil, nil, types.NewTuple(params...), sig.Results(), sig.Variadic()) - } ret := p.toLLVMFunc(sig) - p.typs.Set(sigOrg, ret) + p.typs.Set(sig, ret) return ret }