Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 70 additions & 17 deletions cl/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ var (
enableDbg bool
enableDbgSyms bool
disableInline bool
mayMoreStackHook string

// enableExportRename enables //export to use different C symbol names than Go function names.
// This is for TinyGo compatibility when using -target flag for embedded targets.
Expand Down Expand Up @@ -121,6 +122,12 @@ func EnableTrace(b bool) {
enableCallTracing = b
}

// SetMayMoreStackHook sets the function called at ordinary Go function entry
// when -gcflags=-d=maymorestack=... is supplied.
func SetMayMoreStackHook(name string) {
mayMoreStackHook = name
}

// EnableExportRename enables or disables //export with different C symbol names.
// This is enabled when using -target flag for TinyGo compatibility.
func EnableExportRename(b bool) {
Expand Down Expand Up @@ -152,23 +159,24 @@ type pkgInfo struct {
type none = struct{}

type context struct {
prog llssa.Program
pkg llssa.Package
fn llssa.Function
goFn *ssa.Function
fset *token.FileSet
goProg *ssa.Program
goTyps *types.Package
goPkg *ssa.Package
pyMod string
skips map[string]none
loaded map[*types.Package]*pkgInfo // loaded packages
bvals map[ssa.Value]llssa.Expr // block values
vargs map[*ssa.Alloc][]llssa.Expr // varargs
funcs map[*ssa.Function]llssa.Function
stackDefers map[*ssa.Function]bool
anonDefers map[*ssa.Function]bool
paramDIVars map[*types.Var]llssa.DIVar
prog llssa.Program
pkg llssa.Package
fn llssa.Function
goFn *ssa.Function
fset *token.FileSet
goProg *ssa.Program
goTyps *types.Package
goPkg *ssa.Package
pyMod string
skips map[string]none
loaded map[*types.Package]*pkgInfo // loaded packages
bvals map[ssa.Value]llssa.Expr // block values
vargs map[*ssa.Alloc][]llssa.Expr // varargs
funcs map[*ssa.Function]llssa.Function
stackDefers map[*ssa.Function]bool
anonDefers map[*ssa.Function]bool
maymorestack string
paramDIVars map[*types.Var]llssa.DIVar

patches Patches
blkInfos []blocks.Info
Expand Down Expand Up @@ -562,6 +570,9 @@ func (p *context) compileBlock(b llssa.Builder, block *ssa.BasicBlock, n int, do
if block.Index == 0 && enableCallTracing && !strings.HasPrefix(fn.Name(), "github.com/goplus/llgo/runtime/internal/runtime.Print") {
b.Printf("call " + fn.Name() + "\n\x00")
}
if block.Index == 0 {
p.emitMayMoreStackHook(b)
}
// place here to avoid wrong current-block
if enableDbgSyms && block.Parent().Origin() == nil && block.Index == 0 {
p.debugParams(b, block.Parent())
Expand Down Expand Up @@ -664,6 +675,48 @@ end:
return ret
}

func (p *context) emitMayMoreStackHook(b llssa.Builder) {
if p.goFn == nil || p.goFn.Synthetic != "" {
return
}
hook := p.mayMoreStackHookName()
if hook == "" || hook == p.fn.Name() {
return
}
fn := p.pkg.FuncOf(hook)
if fn == nil {
fn = p.pkg.NewFunc(hook, llssa.NoArgsNoRet, llssa.InGo)
}
b.Call(fn.Expr)
}

func (p *context) mayMoreStackHookName() string {
if p.maymorestack != "" || mayMoreStackHook == "" || p.goTyps == nil {
return p.maymorestack
}
hook := strings.TrimSpace(mayMoreStackHook)
if hook == "" {
return ""
}
pkgPath := llssa.PathOf(p.goTyps)
pkgName := p.goTyps.Name()
if name, ok := strings.CutPrefix(hook, pkgName+"."); ok {
p.maymorestack = pkgPath + "." + name
return p.maymorestack
}
if pkgName == "main" {
if name, ok := strings.CutPrefix(hook, "main."); ok {
p.maymorestack = pkgPath + "." + name
return p.maymorestack
}
}
if strings.HasPrefix(hook, pkgPath+".") {
p.maymorestack = hook
return p.maymorestack
}
return ""
}

const (
RuntimeInit = llssa.PkgRuntime + ".init"
)
Expand Down
3 changes: 3 additions & 0 deletions cmd/internal/base/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ type Command struct {
// Flag is a set of flags specific to this command.
Flag flag.FlagSet

// PassArgs records build flags accepted for compatibility with cmd/go.
PassArgs *PassArgs

// Commands lists the available commands and help topics.
// The order here is the order in which they are printed by 'gop help'.
// Note that subcommands are in general best avoided.
Expand Down
1 change: 1 addition & 0 deletions cmd/internal/base/pass.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ func NewPassArgs(flag *flag.FlagSet) *PassArgs {

func PassBuildFlags(cmd *Command) *PassArgs {
p := NewPassArgs(&cmd.Flag)
cmd.PassArgs = p
p.Bool("n")
// Note: "a" flag removed - now handled by flags.AddBuildFlags()
p.Bool("linkshared", "race", "msan", "asan",
Expand Down
4 changes: 4 additions & 0 deletions cmd/internal/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ func runCmd(cmd *base.Command, args []string) {
fmt.Fprintln(os.Stderr, err)
mockable.Exit(1)
}
if err := flags.UpdatePassBuildConfig(conf, cmd.PassArgs.Args); err != nil {
fmt.Fprintln(os.Stderr, err)
mockable.Exit(1)
}

args = cmd.Flag.Args()

Expand Down
65 changes: 65 additions & 0 deletions cmd/internal/flags/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ package flags
import (
"flag"
"fmt"
"strings"

"github.com/goplus/llgo/cmd/internal/compilerhash"
"github.com/goplus/llgo/internal/build"
"github.com/goplus/llgo/internal/buildenv"
"github.com/goplus/llgo/internal/optlevel"
"github.com/goplus/llgo/internal/shellparse"
)

var OutputFile string
Expand Down Expand Up @@ -272,3 +274,66 @@ func UpdateBuildConfig(conf *build.Config) error {

return nil
}

func UpdatePassBuildConfig(conf *build.Config, args []string) error {
hook, err := mayMoreStackHookFromBuildFlags(args)
if err != nil {
return err
}
conf.MayMoreStack = hook
return nil
}

func mayMoreStackHookFromBuildFlags(args []string) (string, error) {
var hook string
for i := 0; i < len(args); i++ {
arg := args[i]
var gcflags string
switch {
case arg == "-gcflags":
i++
if i >= len(args) {
return "", fmt.Errorf("-gcflags requires an argument")
}
gcflags = args[i]
case strings.HasPrefix(arg, "-gcflags="):
gcflags = strings.TrimPrefix(arg, "-gcflags=")
default:
continue
}
got, err := mayMoreStackHookFromGCFlags(gcflags)
if err != nil {
return "", err
}
if got != "" {
hook = got
}
}
return hook, nil
}

func mayMoreStackHookFromGCFlags(gcflags string) (string, error) {
fields, err := shellparse.Parse(gcflags)
if err != nil {
return "", fmt.Errorf("parse -gcflags: %w", err)
}
for _, field := range fields {
if pattern, rest, ok := strings.Cut(field, "="); ok && !strings.HasPrefix(pattern, "-") {
field = rest
}
if !strings.HasPrefix(field, "-d=") {
continue
}
for _, opt := range strings.Split(strings.TrimPrefix(field, "-d="), ",") {
hook, ok := strings.CutPrefix(opt, "maymorestack=")
if !ok {
continue
}
if hook == "" {
return "", fmt.Errorf("-d=maymorestack requires a function name")
}
return hook, nil
}
}
return "", nil
}
65 changes: 65 additions & 0 deletions cmd/internal/flags/flags_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"flag"
"testing"

"github.com/goplus/llgo/internal/build"
"github.com/goplus/llgo/internal/optlevel"
)

Expand Down Expand Up @@ -69,3 +70,67 @@ func TestBuildOptimizationFlagsMutuallyExclusive(t *testing.T) {
})
}
}

func TestUpdatePassBuildConfigMayMoreStack(t *testing.T) {
tests := []struct {
name string
args []string
want string
}{
{
name: "direct gcflags",
args: []string{"-gcflags=-d=maymorestack=main.mayMoreStack"},
want: "main.mayMoreStack",
},
{
name: "pattern gcflags",
args: []string{"-gcflags=all=-d=maymorestack=main.mayMoreStack"},
want: "main.mayMoreStack",
},
{
name: "space separated gcflags",
args: []string{"-gcflags", "-N -d=maymorestack=main.mayMoreStack -l"},
want: "main.mayMoreStack",
},
{
name: "comma debug options",
args: []string{"-gcflags=-d=checkptr,maymorestack=main.mayMoreStack"},
want: "main.mayMoreStack",
},
{
name: "unrelated gcflags",
args: []string{"-gcflags=-N -l"},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
conf := &build.Config{}
if err := UpdatePassBuildConfig(conf, tt.args); err != nil {
t.Fatalf("UpdatePassBuildConfig() error = %v", err)
}
if conf.MayMoreStack != tt.want {
t.Fatalf("MayMoreStack = %q, want %q", conf.MayMoreStack, tt.want)
}
})
}
}

func TestUpdatePassBuildConfigMayMoreStackErrors(t *testing.T) {
tests := []struct {
name string
args []string
}{
{name: "missing gcflags value", args: []string{"-gcflags"}},
{name: "empty hook", args: []string{"-gcflags=-d=maymorestack="}},
{name: "bad quoting", args: []string{"-gcflags='-d=maymorestack=main.mayMoreStack"}},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := UpdatePassBuildConfig(&build.Config{}, tt.args); err == nil {
t.Fatal("UpdatePassBuildConfig() expected error")
}
})
}
}
5 changes: 5 additions & 0 deletions cmd/internal/install/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ var Cmd = &base.Command{

func init() {
Cmd.Run = runCmd
base.PassBuildFlags(Cmd)
flags.AddCommonFlags(&Cmd.Flag)
flags.AddBuildFlags(&Cmd.Flag)
flags.AddEmbeddedFlags(&Cmd.Flag)
Expand All @@ -51,6 +52,10 @@ func runCmd(cmd *base.Command, args []string) {
fmt.Fprintln(os.Stderr, err)
mockable.Exit(1)
}
if err := flags.UpdatePassBuildConfig(conf, cmd.PassArgs.Args); err != nil {
fmt.Fprintln(os.Stderr, err)
mockable.Exit(1)
}

args = cmd.Flag.Args()
_, err := build.Do(args, conf)
Expand Down
4 changes: 4 additions & 0 deletions cmd/internal/run/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ func runCmdEx(cmd *base.Command, args []string, mode build.Mode) {
fmt.Fprintln(os.Stderr, err)
mockable.Exit(1)
}
if err := flags.UpdatePassBuildConfig(conf, cmd.PassArgs.Args); err != nil {
fmt.Fprintln(os.Stderr, err)
mockable.Exit(1)
}

args = cmd.Flag.Args()
args, runArgs, err := parseRunArgs(args)
Expand Down
5 changes: 5 additions & 0 deletions cmd/internal/test/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ var Cmd = &base.Command{

func init() {
Cmd.Run = runCmd
base.PassBuildFlags(Cmd)
flags.AddCommonFlags(&Cmd.Flag)
flags.AddBuildFlags(&Cmd.Flag)
flags.AddTestFlags(&Cmd.Flag)
Expand All @@ -43,6 +44,10 @@ func runCmd(cmd *base.Command, args []string) {
fmt.Fprintln(os.Stderr, err)
mockable.Exit(1)
}
if err := flags.UpdatePassBuildConfig(conf, cmd.PassArgs.Args); err != nil {
fmt.Fprintln(os.Stderr, err)
mockable.Exit(1)
}

// Match `go test` behavior: set testing.Testing() to true by forcing the
// stdlib testing package's testBinary marker to "1" in test binaries.
Expand Down
2 changes: 2 additions & 0 deletions internal/build/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ type Config struct {
SizeFormat string // size report format: text,json (default text)
SizeLevel string // size aggregation level: full,module,package (default module)
CompilerHash string // metadata hash for the running compiler (development builds only)
MayMoreStack string // function hook from -gcflags=-d=maymorestack=...
// GlobalRewrites specifies compile-time overrides for global string variables.
// Keys are fully qualified package paths (e.g. "main" or "github.com/user/pkg").
// Each Rewrites entry maps variable names to replacement string values. Only
Expand Down Expand Up @@ -281,6 +282,7 @@ func Do(args []string, conf *Config) ([]Package, error) {
cl.EnableDebug(IsDbgEnabled())
cl.EnableDbgSyms(IsDbgSymsEnabled())
cl.EnableTrace(IsTraceEnabled())
cl.SetMayMoreStackHook(conf.MayMoreStack)
llssa.Initialize(llssa.InitAll)

target := &llssa.Target{
Expand Down
Loading
Loading