From 9b742e777be9b097e3db401ad408707d61f3d83a Mon Sep 17 00:00:00 2001 From: xushiwei Date: Wed, 15 May 2024 11:59:53 +0800 Subject: [PATCH] cl: _testpy/gcd --- c/c.go | 16 ++++--- chore/llpyg/llpyg.go | 3 +- cl/_testdata/printf/out.ll | 1 + cl/_testpy/gcd/in.go | 12 ++++++ cl/_testpy/gcd/out.ll | 59 ++++++++++++++++++++++++++ py/long.go | 73 ++++++++++++++++++++++++++++++++ py/math/doc.txt | 31 ++++++++++++++ py/math/gen.go | 85 +++++++++++++++++++++++++++++++++++++- py/math/math.go | 32 -------------- ssa/decl.go | 12 ++++-- ssa/package.go | 11 +++-- ssa/ssa_test.go | 2 +- ssa/type.go | 2 +- ssa/type_cvt.go | 23 ++++++++++- 14 files changed, 311 insertions(+), 51 deletions(-) create mode 100644 cl/_testpy/gcd/in.go create mode 100644 cl/_testpy/gcd/out.ll create mode 100644 py/long.go create mode 100644 py/math/doc.txt diff --git a/c/c.go b/c/c.go index 6db9e572..500d29fe 100644 --- a/c/c.go +++ b/c/c.go @@ -25,12 +25,16 @@ const ( ) type ( - Char = int8 - Int = C.int - Uint = C.uint - Float = float32 - Pointer = unsafe.Pointer - FilePtr = unsafe.Pointer + Char = int8 + Int = C.int + Uint = C.uint + Long = int32 + Ulong = uint32 + LongLong = int64 + UlongLong = uint64 + Float = float32 + Pointer = unsafe.Pointer + FilePtr = unsafe.Pointer ) type integer interface { diff --git a/chore/llpyg/llpyg.go b/chore/llpyg/llpyg.go index 4cd6ab3c..a95ee7a2 100644 --- a/chore/llpyg/llpyg.go +++ b/chore/llpyg/llpyg.go @@ -29,6 +29,7 @@ import ( "strings" "github.com/goplus/gogen" + "github.com/goplus/llgo/ssa" ) type symbol struct { @@ -146,7 +147,7 @@ func (ctx *context) genParams(pkg *gogen.Package, sig string) (*types.Tuple, boo } if strings.HasPrefix(part, "*") { if part[1] != '*' { - list = append(list, pkg.NewParam(0, genName(part[1:], 0), types.NewSlice(objPtr))) + list = append(list, ssa.VArg()) return types.NewTuple(list...), true, false } return types.NewTuple(list...), false, false diff --git a/cl/_testdata/printf/out.ll b/cl/_testdata/printf/out.ll index fbd81b42..a9fa07ad 100644 --- a/cl/_testdata/printf/out.ll +++ b/cl/_testdata/printf/out.ll @@ -6,6 +6,7 @@ source_filename = "main" @__llgo_argc = global ptr null @__llgo_argv = global ptr null + define void @main.init() { _llgo_0: %0 = load i1, ptr @"main.init$guard", align 1 diff --git a/cl/_testpy/gcd/in.go b/cl/_testpy/gcd/in.go new file mode 100644 index 00000000..309cdf3c --- /dev/null +++ b/cl/_testpy/gcd/in.go @@ -0,0 +1,12 @@ +package main + +import ( + "github.com/goplus/llgo/c" + "github.com/goplus/llgo/py" + "github.com/goplus/llgo/py/math" +) + +func main() { + x := math.Gcd(py.Long(60), py.Long(20), py.Long(25)) + c.Printf(c.Str("gcd(60, 20, 25) = %d\n"), x.Long()) +} diff --git a/cl/_testpy/gcd/out.ll b/cl/_testpy/gcd/out.ll new file mode 100644 index 00000000..ccee1b2f --- /dev/null +++ b/cl/_testpy/gcd/out.ll @@ -0,0 +1,59 @@ +; ModuleID = 'main' +source_filename = "main" + +@"main.init$guard" = global ptr null +@__llgo_argc = global ptr null +@__llgo_argv = global ptr null +@__llgo_py.math.gcd = linkonce global ptr null +@0 = private unnamed_addr constant [22 x i8] c"gcd(60, 20, 25) = %d\0A\00", align 1 +@__llgo_py.math = external global ptr +@1 = private unnamed_addr constant [4 x i8] c"gcd\00", align 1 + +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 + call void @"github.com/goplus/llgo/py/math.init"() + %1 = load ptr, ptr @__llgo_py.math, align 8 + call void (ptr, ...) @llgoLoadPyModSyms(ptr %1, ptr @1, ptr @__llgo_py.math.gcd, ptr null) + br label %_llgo_2 + +_llgo_2: ; preds = %_llgo_1, %_llgo_0 + ret void +} + +define void @main(i32 %0, ptr %1) { +_llgo_0: + call void @Py_Initialize() + store i32 %0, ptr @__llgo_argc, align 4 + store ptr %1, ptr @__llgo_argv, align 8 + call void @"github.com/goplus/llgo/internal/runtime.init"() + call void @main.init() + %2 = call ptr @PyLong_FromLong(i32 60) + %3 = call ptr @PyLong_FromLong(i32 20) + %4 = call ptr @PyLong_FromLong(i32 25) + %5 = load ptr, ptr @__llgo_py.math.gcd, align 8 + %6 = call ptr (ptr, ...) @PyObject_CallFunctionObjArgs(ptr %5, ptr %2, ptr %3, ptr %4, ptr null) + %7 = call i32 @PyLong_AsLong(ptr %6) + %8 = call i32 (ptr, ...) @printf(ptr @0, i32 %7) + ret void +} + +declare void @"github.com/goplus/llgo/py/math.init"() + +declare void @"github.com/goplus/llgo/internal/runtime.init"() + +declare ptr @PyLong_FromLong(i32) + +declare ptr @PyObject_CallFunctionObjArgs(ptr, ...) + +declare i32 @PyLong_AsLong(ptr) + +declare i32 @printf(ptr, ...) + +declare void @llgoLoadPyModSyms(ptr, ...) + +declare void @Py_Initialize() diff --git a/py/long.go b/py/long.go new file mode 100644 index 00000000..a68d8286 --- /dev/null +++ b/py/long.go @@ -0,0 +1,73 @@ +/* + * 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 py + +import ( + _ "unsafe" + + "github.com/goplus/llgo/c" +) + +// https://docs.python.org/3/c-api/long.html + +//go:linkname Long C.PyLong_FromLong +func Long(v c.Long) *Object + +//go:linkname LongLong C.PyLong_FromLongLong +func LongLong(v c.LongLong) *Object + +//go:linkname Ulong C.PyLong_FromUnsignedLong +func Ulong(v c.Ulong) *Object + +//go:linkname UlongLong C.PyLong_FromUnsignedLongLong +func UlongLong(v c.UlongLong) *Object + +//go:linkname Uintptr C.PyLong_FromSize_t +func Uintptr(v uintptr) *Object + +//go:linkname LongFromFloat64 C.PyLong_FromDouble +func LongFromFloat64(v float64) *Object + +//go:linkname LongFromVoidPtr C.PyLong_FromVoidPtr +func LongFromVoidPtr(v c.Pointer) *Object + +//go:linkname LongFromCStr C.PyLong_FromString +func LongFromCStr(v *c.Char, pend **c.Char, base c.Int) *Object + +//go:linkname LongFromUnicode C.PyLong_FromUnicodeObject +func LongFromUnicode(v *Object, base c.Int) *Object + +// llgo:link (*Object).Long C.PyLong_AsLong +func (l *Object) Long() c.Long { return 0 } + +// llgo:link (*Object).LongLong C.PyLong_AsLongLong +func (l *Object) LongLong() c.LongLong { return 0 } + +// llgo:link (*Object).Ulong C.PyLong_AsUnsignedLong +func (l *Object) Ulong() c.Ulong { return 0 } + +// llgo:link (*Object).UlongLong C.PyLong_AsUnsignedLongLong +func (l *Object) UlongLong() c.UlongLong { return 0 } + +// llgo:link (*Object).Uintptr C.PyLong_AsSize_t +func (l *Object) Uintptr() uintptr { return 0 } + +// llgo:link (*Object).LongAsFloat64 C.PyLong_AsDouble +func (l *Object) LongAsFloat64() float64 { return 0 } + +// llgo:link (*Object).LongAsVoidPtr C.PyLong_AsVoidPtr +func (l *Object) LongAsVoidPtr() c.Pointer { return nil } diff --git a/py/math/doc.txt b/py/math/doc.txt new file mode 100644 index 00000000..7ba48646 --- /dev/null +++ b/py/math/doc.txt @@ -0,0 +1,31 @@ +// Unlike the built-in ** operator, math.pow() converts both its arguments to type +// float. Use ** or the built-in pow() function for computing exact integer powers. +// +//go:linkname Pow py.pow +func Pow(x, y *py.Object) *py.Object + +// Return the sine of x radians. +// +//go:linkname Sin py.sin +func Sin(x *py.Object) *py.Object + +// Return the hyperbolic sine of x. +// +//go:linkname Sinh py.sinh +func Sinh(x *py.Object) *py.Object + +// Return the base-2 logarithm of x. This is usually more accurate than log(x, 2). +// +//go:linkname Log2 py.log2 +func Log2(x *py.Object) *py.Object + +// Return the base-10 logarithm of x. This is usually more accurate than log(x, 10). +// +//go:linkname Log10 py.log10 +func Log10(x *py.Object) *py.Object + +// Return the fractional and integer parts of x. Both results carry the sign of +// x and are floats. +// +//go:linkname Modf py.modf +func Modf(x *py.Object) *py.Object diff --git a/py/math/gen.go b/py/math/gen.go index 48163fed..34315fdb 100644 --- a/py/math/gen.go +++ b/py/math/gen.go @@ -174,7 +174,90 @@ func Gamma(x *py.Object) *py.Object // Greatest Common Divisor. // //go:linkname Gcd py.gcd -func Gcd(integers ...*py.Object) *py.Object +func Gcd(__llgo_va_list ...interface{}) *py.Object + +// Return True if x is neither an infinity nor a NaN, and False otherwise. +// +//go:linkname Isfinite py.isfinite +func Isfinite(x *py.Object) *py.Object + +// Return True if x is a positive or negative infinity, and False otherwise. +// +//go:linkname Isinf py.isinf +func Isinf(x *py.Object) *py.Object + +// Return True if x is a NaN (not a number), and False otherwise. +// +//go:linkname Isnan py.isnan +func Isnan(x *py.Object) *py.Object + +// Return the integer part of the square root of the input. +// +//go:linkname Isqrt py.isqrt +func Isqrt(n *py.Object) *py.Object + +// Least Common Multiple. +// +//go:linkname Lcm py.lcm +func Lcm(__llgo_va_list ...interface{}) *py.Object + +// Return x * (2**i). +// +// This is essentially the inverse of frexp(). +// +//go:linkname Ldexp py.ldexp +func Ldexp(x *py.Object, i *py.Object) *py.Object + +// Natural logarithm of absolute value of Gamma function at x. +// +//go:linkname Lgamma py.lgamma +func Lgamma(x *py.Object) *py.Object + +// Return the base 10 logarithm of x. +// +//go:linkname Log10 py.log10 +func Log10(x *py.Object) *py.Object + +// Return the base 2 logarithm of x. +// +//go:linkname Log2 py.log2 +func Log2(x *py.Object) *py.Object + +// Return the fractional and integer parts of x. +// +// Both results carry the sign of x and are floats. +// +//go:linkname Modf py.modf +func Modf(x *py.Object) *py.Object + +// Return x**y (x to the power of y). +// +//go:linkname Pow py.pow +func Pow(x *py.Object, y *py.Object) *py.Object + +// Convert angle x from degrees to radians. +// +//go:linkname Radians py.radians +func Radians(x *py.Object) *py.Object + +// Difference between x and the closest integer multiple of y. +// +// Return x - n*y where n*y is the closest integer multiple of y. +// In the case where x is exactly halfway between two multiples of +// y, the nearest even value of n is used. The result is always exact. +// +//go:linkname Remainder py.remainder +func Remainder(x *py.Object, y *py.Object) *py.Object + +// Return the sine of x (measured in radians). +// +//go:linkname Sin py.sin +func Sin(x *py.Object) *py.Object + +// Return the hyperbolic sine of x. +// +//go:linkname Sinh py.sinh +func Sinh(x *py.Object) *py.Object // Return the square root of x. // diff --git a/py/math/math.go b/py/math/math.go index 6f7fcb08..452ff2c6 100644 --- a/py/math/math.go +++ b/py/math/math.go @@ -27,22 +27,6 @@ import ( //go:linkname Pi py.pi var Pi *py.Object -// Unlike the built-in ** operator, math.pow() converts both its arguments to type -// float. Use ** or the built-in pow() function for computing exact integer powers. -// -//go:linkname Pow py.pow -func Pow(x, y *py.Object) *py.Object - -// Return the sine of x radians. -// -//go:linkname Sin py.sin -func Sin(x *py.Object) *py.Object - -// Return the hyperbolic sine of x. -// -//go:linkname Sinh py.sinh -func Sinh(x *py.Object) *py.Object - // With one argument, return the natural logarithm of x (to base e). // //go:linkname Log py.log @@ -60,22 +44,6 @@ func LogOf(x, base *py.Object) *py.Object //go:linkname Log1p py.log1p func Log1p(x *py.Object) *py.Object -// Return the base-2 logarithm of x. This is usually more accurate than log(x, 2). -// -//go:linkname Log2 py.log2 -func Log2(x *py.Object) *py.Object - -// Return the base-10 logarithm of x. This is usually more accurate than log(x, 10). -// -//go:linkname Log10 py.log10 -func Log10(x *py.Object) *py.Object - -// Return the fractional and integer parts of x. Both results carry the sign of -// x and are floats. -// -//go:linkname Modf py.modf -func Modf(x *py.Object) *py.Object - // Return the Euclidean norm, sqrt(sum(x**2 for x in coordinates)). This is the // length of the vector from the origin to the point given by the coordinates. // diff --git a/ssa/decl.go b/ssa/decl.go index ca857cb1..12e67189 100644 --- a/ssa/decl.go +++ b/ssa/decl.go @@ -33,15 +33,19 @@ const ( ) func VArg() *types.Var { - return types.NewParam(0, nil, NameValist, types.Typ[types.Invalid]) + return types.NewParam(0, nil, NameValist, types.NewSlice(tyAny)) } func IsVArg(arg *types.Var) bool { return arg.Name() == NameValist } -func HasVArg(t *types.Tuple, n int) bool { - return n > 0 && IsVArg(t.At(n-1)) +func HasVArg(t *types.Tuple, n int, sig *types.Signature) bool { + has := n > 0 && IsVArg(t.At(n-1)) + if has && !sig.Variadic() { // TODO(xsw): remove this check + panic("HasVArg: varg must mark as variadic in signature") + } + return has } // ----------------------------------------------------------------------------- @@ -196,7 +200,7 @@ func newParams(fn Type, prog Program) (params []Type, hasVArg bool) { sig := fn.raw.Type.(*types.Signature) in := sig.Params() if n := in.Len(); n > 0 { - if hasVArg = HasVArg(in, n); hasVArg { + if hasVArg = HasVArg(in, n, sig); hasVArg { n-- } params = make([]Type, n) diff --git a/ssa/package.go b/ssa/package.go index d04f3267..63054a5e 100644 --- a/ssa/package.go +++ b/ssa/package.go @@ -532,7 +532,7 @@ func (p Program) tyCallFunctionObjArgs() *types.Signature { paramObjPtr := p.paramObjPtr() params := types.NewTuple(paramObjPtr, VArg()) results := types.NewTuple(paramObjPtr) - p.callFOArgs = types.NewSignatureType(nil, nil, nil, params, results, false) + p.callFOArgs = types.NewSignatureType(nil, nil, nil, params, results, true) } return p.callFOArgs } @@ -576,7 +576,7 @@ func (p Program) tyLoadPyModSyms() *types.Signature { objPtr := p.PyObjectPtr().raw.Type paramObjPtr := types.NewParam(token.NoPos, nil, "mod", objPtr) params := types.NewTuple(paramObjPtr, VArg()) - p.loadPyModS = types.NewSignatureType(nil, nil, nil, params, nil, false) + p.loadPyModS = types.NewSignatureType(nil, nil, nil, params, nil, true) } return p.loadPyModS } @@ -647,8 +647,11 @@ func (b Builder) pyCall(fn Expr, args []Expr) (ret Expr) { call := pkg.pyFunc("PyObject_CallNoArgs", prog.tyCallNoArgs()) ret = b.Call(call, fn) case 1: - call := pkg.pyFunc("PyObject_CallOneArg", prog.tyCallOneArg()) - ret = b.Call(call, fn, args[0]) + if !sig.Variadic() { + call := pkg.pyFunc("PyObject_CallOneArg", prog.tyCallOneArg()) + return b.Call(call, fn, args[0]) + } + fallthrough default: call := pkg.pyFunc("PyObject_CallFunctionObjArgs", prog.tyCallFunctionObjArgs()) n = len(args) diff --git a/ssa/ssa_test.go b/ssa/ssa_test.go index b2bfe311..409ec773 100644 --- a/ssa/ssa_test.go +++ b/ssa/ssa_test.go @@ -399,7 +399,7 @@ func TestPrintf(t *testing.T) { pchar := types.NewPointer(types.Typ[types.Int8]) params := types.NewTuple(types.NewVar(0, nil, "format", pchar), VArg()) rets := types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int32])) - sig := types.NewSignatureType(nil, nil, nil, params, rets, false) + sig := types.NewSignatureType(nil, nil, nil, params, rets, true) pkg.NewFunc("printf", sig, InGo) assertPkg(t, pkg, `; ModuleID = 'foo/bar' source_filename = "foo/bar" diff --git a/ssa/type.go b/ssa/type.go index 735a3d90..987fe436 100644 --- a/ssa/type.go +++ b/ssa/type.go @@ -313,7 +313,7 @@ func (p Program) toLLVMTypes(t *types.Tuple, n int) (ret []llvm.Type) { func (p Program) toLLVMFunc(sig *types.Signature) llvm.Type { tParams := sig.Params() n := tParams.Len() - hasVArg := HasVArg(tParams, n) + hasVArg := HasVArg(tParams, n, sig) if hasVArg { n-- } diff --git a/ssa/type_cvt.go b/ssa/type_cvt.go index e8bd1c3d..9f6b9aee 100644 --- a/ssa/type_cvt.go +++ b/ssa/type_cvt.go @@ -66,6 +66,27 @@ func (p Program) FuncDecl(sig *types.Signature, bg Background) Type { return &aType{p.toLLVMFunc(sig), rawType{sig}, vkFuncDecl} } +/* +// cvtCxFunc converts a C extended function type into raw type. +func cvtCxFunc(sig *types.Signature, recv *types.Var) *types.Signature { + if sig.Variadic() { + // convert printf-like function type + tParams := sig.Params() + n := tParams.Len() + params := make([]*types.Var, n) + n-- + for i := 0; i < n; i++ { + params[i] = tParams.At(i) + } + params[n] = VArg() + sig = types.NewSignatureType(nil, nil, nil, types.NewTuple(params...), sig.Results(), true) + panic("todo") + } + sig = FuncAddCtx(recv, sig) + return sig +} +*/ + // Closure creates a closture type for a function. func (p Program) Closure(fn Type) Type { sig := fn.raw.Type.(*types.Signature) @@ -152,7 +173,7 @@ func (p goTypes) cvtFunc(sig *types.Signature, recv *types.Var) (raw *types.Sign params, cvt1 := p.cvtTuple(sig.Params()) results, cvt2 := p.cvtTuple(sig.Results()) if cvt1 || cvt2 { - return types.NewSignatureType(nil, nil, nil, params, results, sig.Variadic()) + return types.NewSignatureType(nil, nil, nil, params, results, false) } return sig }